lottie-player : Initial draft for lottie-player library 96/183796/6
authorsubhransu mohanty <sub.mohanty@samsung.com>
Wed, 11 Jul 2018 06:24:42 +0000 (15:24 +0900)
committerHermet Park <chuneon.park@samsung.com>
Wed, 11 Jul 2018 10:51:01 +0000 (10:51 +0000)
Change-Id: I8019e13863ef635fc011f0e7b82c692eaa01201d

127 files changed:
.gitignore [new file with mode: 0644]
README.txt [new file with mode: 0644]
example/demo.cpp [new file with mode: 0644]
example/evasapp.cpp [new file with mode: 0644]
example/evasapp.h [new file with mode: 0644]
example/lottieview.cpp [new file with mode: 0644]
example/lottieview.h [new file with mode: 0644]
example/lottieviewtest.cpp [new file with mode: 0644]
example/meson.build [new file with mode: 0644]
example/pathtest.cpp [new file with mode: 0644]
example/resource/acrobatics.json [new file with mode: 0644]
example/resource/confetti.json [new file with mode: 0644]
example/resource/crunches.json [new file with mode: 0755]
example/resource/data.json [new file with mode: 0755]
example/resource/emoji_wink.json [new file with mode: 0644]
example/resource/glow_loading.json [new file with mode: 0644]
example/resource/icon_animation.json [new file with mode: 0755]
example/resource/mask.json [new file with mode: 0755]
example/resource/material_wave_loading.json [new file with mode: 0644]
example/resource/polystar_star_with_roundness_points_stroke_99.json [new file with mode: 0755]
example/resource/rowing_machine.json [new file with mode: 0755]
example/resource/running.json [new file with mode: 0755]
example/resource/star_jumps.json [new file with mode: 0755]
example/resource/walking.json [new file with mode: 0755]
example/resource/yoga.json [new file with mode: 0755]
inc/lottieplayer.h [new file with mode: 0644]
inc/meson.build [new file with mode: 0644]
meson.build [new file with mode: 0644]
meson_options.txt [new file with mode: 0644]
src/lottie/lottieitem.cpp [new file with mode: 0644]
src/lottie/lottieitem.h [new file with mode: 0644]
src/lottie/lottieloader.cpp [new file with mode: 0644]
src/lottie/lottieloader.h [new file with mode: 0644]
src/lottie/lottiemodel.cpp [new file with mode: 0644]
src/lottie/lottiemodel.h [new file with mode: 0644]
src/lottie/lottieparser.cpp [new file with mode: 0644]
src/lottie/lottieparser.h [new file with mode: 0644]
src/lottie/lottieplayer.cpp [new file with mode: 0644]
src/lottie/meson.build [new file with mode: 0644]
src/lottie/rapidjson/allocators.h [new file with mode: 0644]
src/lottie/rapidjson/cursorstreamwrapper.h [new file with mode: 0644]
src/lottie/rapidjson/document.h [new file with mode: 0644]
src/lottie/rapidjson/encodedstream.h [new file with mode: 0644]
src/lottie/rapidjson/encodings.h [new file with mode: 0644]
src/lottie/rapidjson/error/en.h [new file with mode: 0644]
src/lottie/rapidjson/error/error.h [new file with mode: 0644]
src/lottie/rapidjson/filereadstream.h [new file with mode: 0644]
src/lottie/rapidjson/filewritestream.h [new file with mode: 0644]
src/lottie/rapidjson/fwd.h [new file with mode: 0644]
src/lottie/rapidjson/internal/biginteger.h [new file with mode: 0644]
src/lottie/rapidjson/internal/diyfp.h [new file with mode: 0644]
src/lottie/rapidjson/internal/dtoa.h [new file with mode: 0644]
src/lottie/rapidjson/internal/ieee754.h [new file with mode: 0644]
src/lottie/rapidjson/internal/itoa.h [new file with mode: 0644]
src/lottie/rapidjson/internal/meta.h [new file with mode: 0644]
src/lottie/rapidjson/internal/pow10.h [new file with mode: 0644]
src/lottie/rapidjson/internal/regex.h [new file with mode: 0644]
src/lottie/rapidjson/internal/stack.h [new file with mode: 0644]
src/lottie/rapidjson/internal/strfunc.h [new file with mode: 0644]
src/lottie/rapidjson/internal/strtod.h [new file with mode: 0644]
src/lottie/rapidjson/internal/swap.h [new file with mode: 0644]
src/lottie/rapidjson/istreamwrapper.h [new file with mode: 0644]
src/lottie/rapidjson/memorybuffer.h [new file with mode: 0644]
src/lottie/rapidjson/memorystream.h [new file with mode: 0644]
src/lottie/rapidjson/meson.build [new file with mode: 0644]
src/lottie/rapidjson/msinttypes/inttypes.h [new file with mode: 0644]
src/lottie/rapidjson/msinttypes/stdint.h [new file with mode: 0644]
src/lottie/rapidjson/ostreamwrapper.h [new file with mode: 0644]
src/lottie/rapidjson/pointer.h [new file with mode: 0644]
src/lottie/rapidjson/prettywriter.h [new file with mode: 0644]
src/lottie/rapidjson/rapidjson.h [new file with mode: 0644]
src/lottie/rapidjson/reader.h [new file with mode: 0644]
src/lottie/rapidjson/schema.h [new file with mode: 0644]
src/lottie/rapidjson/stream.h [new file with mode: 0644]
src/lottie/rapidjson/stringbuffer.h [new file with mode: 0644]
src/lottie/rapidjson/writer.h [new file with mode: 0644]
src/meson.build [new file with mode: 0644]
src/vector/freetype/meson.build [new file with mode: 0644]
src/vector/freetype/v_ft_math.cpp [new file with mode: 0644]
src/vector/freetype/v_ft_math.h [new file with mode: 0644]
src/vector/freetype/v_ft_raster.cpp [new file with mode: 0644]
src/vector/freetype/v_ft_raster.h [new file with mode: 0644]
src/vector/freetype/v_ft_stroker.cpp [new file with mode: 0644]
src/vector/freetype/v_ft_stroker.h [new file with mode: 0644]
src/vector/freetype/v_ft_types.h [new file with mode: 0644]
src/vector/meson.build [new file with mode: 0644]
src/vector/vbezier.cpp [new file with mode: 0644]
src/vector/vbezier.h [new file with mode: 0644]
src/vector/vbitmap.cpp [new file with mode: 0644]
src/vector/vbitmap.h [new file with mode: 0644]
src/vector/vbrush.cpp [new file with mode: 0644]
src/vector/vbrush.h [new file with mode: 0644]
src/vector/vcompositionfunctions.cpp [new file with mode: 0644]
src/vector/vdasher.cpp [new file with mode: 0644]
src/vector/vdasher.h [new file with mode: 0644]
src/vector/vdebug.cpp [new file with mode: 0644]
src/vector/vdebug.h [new file with mode: 0644]
src/vector/vdrawhelper.cpp [new file with mode: 0644]
src/vector/vdrawhelper.h [new file with mode: 0644]
src/vector/vdrawhelper_neon.cpp [new file with mode: 0644]
src/vector/vdrawhelper_sse2.cpp [new file with mode: 0644]
src/vector/velapsedtimer.cpp [new file with mode: 0644]
src/vector/velapsedtimer.h [new file with mode: 0644]
src/vector/vglobal.h [new file with mode: 0644]
src/vector/vinterpolator.cpp [new file with mode: 0644]
src/vector/vinterpolator.h [new file with mode: 0644]
src/vector/vmatrix.cpp [new file with mode: 0644]
src/vector/vmatrix.h [new file with mode: 0644]
src/vector/vpainter.cpp [new file with mode: 0644]
src/vector/vpainter.h [new file with mode: 0644]
src/vector/vpath.cpp [new file with mode: 0644]
src/vector/vpath.h [new file with mode: 0644]
src/vector/vpathmesure.cpp [new file with mode: 0644]
src/vector/vpathmesure.h [new file with mode: 0644]
src/vector/vpoint.h [new file with mode: 0644]
src/vector/vraster.cpp [new file with mode: 0644]
src/vector/vraster.h [new file with mode: 0644]
src/vector/vrect.cpp [new file with mode: 0644]
src/vector/vrect.h [new file with mode: 0644]
src/vector/vregion.cpp [new file with mode: 0644]
src/vector/vregion.h [new file with mode: 0644]
src/vector/vrle.cpp [new file with mode: 0644]
src/vector/vrle.h [new file with mode: 0644]
test/meson.build [new file with mode: 0644]
test/ssgtestsuite.cpp [new file with mode: 0644]
test/testftraster.cpp [new file with mode: 0644]
test/testsgregion.cpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f13e7a5
--- /dev/null
@@ -0,0 +1,5 @@
+build
+*.creator*
+*.config
+*.files
+*.includes
\ No newline at end of file
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..c079df8
--- /dev/null
@@ -0,0 +1,11 @@
+BUILD INSTRUCTION
+=================
+1. install meson build system. ( follow instruction in this link http://mesonbuild.com/Getting-meson.html )
+2. install ninja build tool    (https://ninja-build.org/)
+3. make a build directory in ssg/ flder
+4. run meson build/
+
+RUN INSTRUCTION
+===============
+1. run ninja inside build/ folder
+2. to run example invoke ./build/example/ssgdemo
diff --git a/example/demo.cpp b/example/demo.cpp
new file mode 100644 (file)
index 0000000..fcf544b
--- /dev/null
@@ -0,0 +1,41 @@
+#include "evasapp.h"
+#include "lottieview.h"
+#include<iostream>
+#include <stdio.h>
+using namespace std;
+
+static void
+onExitCb(void *data)
+{
+    LottieView *view = (LottieView *)data;
+    delete view;
+}
+
+int
+main(void)
+{
+   EvasApp *app = new EvasApp(800, 800);
+   app->setup();
+
+   std::string filePath = DEMO_DIR;
+   filePath +="mask.json";
+
+   LottieView *view = new LottieView(app->evas());
+   view->setFilePath(filePath.c_str());
+   view->setPos(0, 0);
+   view->setSize(800, 800);
+   view->show();
+   view->play();
+   view->loop(true);
+   view->setRepeatMode(LottieView::RepeatMode::Reverse);
+
+   app->addExitCb(onExitCb, view);
+   app->run();
+   delete app;
+   return 0;
+}
+
+
+
+
+
diff --git a/example/evasapp.cpp b/example/evasapp.cpp
new file mode 100644 (file)
index 0000000..4f4762e
--- /dev/null
@@ -0,0 +1,70 @@
+#include "evasapp.h"
+
+static void
+_on_resize(Ecore_Evas *ee)
+{
+   EvasApp *app = (EvasApp *)ecore_evas_data_get(ee, "app");
+   int w, h;
+   ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+   app->resize(w, h);
+   if (app->mResizeCb)
+       app->mResizeCb(app->mResizeData);
+}
+
+static void
+_on_delete(Ecore_Evas *ee)
+{
+   EvasApp *app = (EvasApp *)ecore_evas_data_get(ee, "app");
+   if (app->mExitCb)
+       app->mExitCb(app->mExitData);
+
+   ecore_main_loop_quit();
+   ecore_evas_free(ee);
+}
+
+EvasApp::EvasApp(int w, int h)
+{
+    if (!ecore_evas_init())
+     return;
+    mw = w;
+    mh = h;
+    //setenv("ECORE_EVAS_ENGINE", "opengl_x11", 1);
+    mEcoreEvas = ecore_evas_new(NULL, 0, 0, mw, mh, NULL);
+    if (!mEcoreEvas) return;
+}
+
+void
+EvasApp::setup()
+{
+    ecore_evas_data_set(mEcoreEvas, "app", this);
+    ecore_evas_callback_resize_set(mEcoreEvas, _on_resize);
+    ecore_evas_callback_delete_request_set(mEcoreEvas, _on_delete);
+
+    ecore_evas_show(mEcoreEvas);
+    mEvas = ecore_evas_get(mEcoreEvas);
+    mBackground = evas_object_rectangle_add(mEvas);
+    evas_object_color_set(mBackground, 70, 70, 70, 255);
+    evas_object_show(mBackground);
+
+    mVg = evas_object_vg_add(mEvas);
+    evas_object_show(mVg);
+
+    mRoot = evas_vg_container_add(mVg);
+    evas_object_vg_root_node_set(mVg, mRoot);
+}
+
+void
+EvasApp::resize(int w, int h)
+{
+    evas_object_resize(mBackground, w, h);
+    evas_object_resize(mVg, w, h);
+    mw = w;
+    mh = h;
+}
+
+void EvasApp::run()
+{
+    resize(mw, mh);
+    ecore_main_loop_begin();
+    ecore_evas_shutdown();
+}
diff --git a/example/evasapp.h b/example/evasapp.h
new file mode 100644 (file)
index 0000000..c7d18cf
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef EVASAPP_H
+#define EVASAPP_H
+
+#ifndef EFL_BETA_API_SUPPORT
+#define EFL_BETA_API_SUPPORT
+#endif
+
+#ifndef EFL_EO_API_SUPPORT
+#define EFL_EO_API_SUPPORT
+#endif
+
+#include <Eo.h>
+#include <Efl.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+
+typedef void (*appCb)(void *userData);
+class EvasApp
+{
+public:
+    EvasApp(int w, int h);
+    void setup();
+    void resize(int w, int h);
+    int width() const{ return mw;}
+    int height() const{ return mh;}
+    void run();
+    Ecore_Evas * ee() const{return mEcoreEvas;}
+    Evas * evas() const {return mEvas;}
+    Efl_VG * root() const {return mRoot;}
+    void addExitCb(appCb exitcb, void *data) {mExitCb = exitcb; mExitData = data;}
+    void addResizeCb(appCb resizecb, void *data) {mResizeCb = resizecb; mResizeData = data;}
+public:
+    int           mw;
+    int           mh;
+    Ecore_Evas   *mEcoreEvas;
+    Evas         *mEvas;
+    Efl_VG       *mRoot;
+    Evas_Object  *mVg;
+    Evas_Object  *mBackground;
+    appCb        mResizeCb;
+    void        *mResizeData;
+    appCb        mExitCb;
+    void        *mExitData;
+};
+#endif //EVASAPP_H
diff --git a/example/lottieview.cpp b/example/lottieview.cpp
new file mode 100644 (file)
index 0000000..de145ef
--- /dev/null
@@ -0,0 +1,225 @@
+#include"lottieview.h"
+#include"lottieplayer.h"
+
+static Eina_Bool
+animator(void *data , double pos)
+{
+    LottieView *view = static_cast<LottieView *>(data);
+    view->seek(pos);
+    if (pos == 1.0) {
+      view->finished();
+      return EINA_FALSE;
+    }
+    return EINA_TRUE;
+}
+
+void LottieView::createVgNode(LOTNode *node, Efl_VG *parent)
+{
+    Efl_VG *shape = evas_vg_shape_add(parent);
+    // update the path
+    const float *data = node->mPath.ptPtr;
+    for(int i=0; i <node->mPath.elmCount; i++) {
+        switch (node->mPath.elmPtr[i]) {
+        case 0:  //moveTo
+        {
+            evas_vg_shape_append_move_to(shape, data[0], data[1]);
+            data += 2;
+            break;
+        }
+        case 1:
+        {
+            evas_vg_shape_append_line_to(shape, data[0], data[1]);
+            data += 2;
+            break;
+        }
+        case 2:
+        {
+            evas_vg_shape_append_cubic_to(shape, data[0], data[1], data[2], data[3], data[4], data[5]);
+            data += 6;
+            break;
+        }
+        case 3:
+        {
+            evas_vg_shape_append_close(shape);
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    if (node->mStroke.enable) {
+        evas_vg_shape_stroke_width_set(shape, node->mStroke.width);
+        //evas_vg_shape_stroke_cap_set(shape, int(node->mStroke.cap));
+        //evas_vg_shape_stroke_join_set(shape, int(node->mStroke.join));
+        //evas_vg_shape_stroke_meter_limit_set(shape, node->mStroke.meterLimit);
+    }
+    // update paint info
+    if (node->mType == LOTNode::BrushSolid) {
+        int r = (node->mColor.r * node->mColor.a)/255;
+        int g = (node->mColor.g * node->mColor.a)/255;
+        int b = (node->mColor.b * node->mColor.a)/255;
+        int a = node->mColor.a;
+        if (node->mStroke.enable) {
+            evas_vg_shape_stroke_color_set(shape, r, g, b, a);
+        } else {
+           evas_vg_node_color_set(shape, r, g, b, a);
+        }
+
+    } else if (node->mType == LOTNode::BrushGradient) {
+        //TODO fill the gradient info
+    }
+}
+
+void LottieView::update(const std::vector<LOTNode *> &renderList)
+{
+    Efl_VG *root = evas_vg_container_add(mVg);
+    for(auto i : renderList) {
+        createVgNode(i, root);
+    }
+    evas_object_vg_root_node_set(mVg, root);
+}
+
+LottieView::LottieView(Evas *evas, bool renderMode)
+{
+    mPalying = false;
+    mReverse = false;
+    mRepeatCount = 0;
+    mRepeatMode = LottieView::RepeatMode::Restart;
+    mLoop = false;
+    mSpeed = 1;
+
+    mEvas = evas;
+    mPlayer = new LOTPlayer();
+    mRenderMode = renderMode;
+
+    if (mRenderMode) {
+        mImage = evas_object_image_filled_add(evas);
+        evas_object_image_colorspace_set(mImage, EVAS_COLORSPACE_ARGB8888);
+        evas_object_image_alpha_set(mImage, EINA_TRUE);
+    } else {
+        mVg = evas_object_vg_add(evas);
+    }
+}
+
+LottieView::~LottieView()
+{
+    ecore_animator_del(mAnimator);
+    delete mPlayer;
+}
+
+void LottieView::show()
+{
+    if (mRenderMode) {
+        evas_object_show(mImage);
+    } else {
+        evas_object_show(mVg);
+    }
+    seek(0);
+}
+
+void LottieView::hide()
+{
+    if (mRenderMode) {
+        evas_object_hide(mImage);
+    } else {
+        evas_object_hide(mVg);
+    }
+}
+
+void LottieView::seek(float pos)
+{
+    if (mPalying && mReverse)
+        pos = 1.0 - pos;
+
+    if (mRenderMode) {
+        LOTBuffer buf;
+        buf.buffer = (uint32_t *)evas_object_image_data_get(mImage, EINA_TRUE);
+        buf.bytesPerLine =  evas_object_image_stride_get(mImage);
+        evas_object_image_size_get(mImage, &buf.width, &buf.height);
+        bool changed = mPlayer->renderSync(pos, buf);
+        evas_object_image_data_set(mImage, buf.buffer);
+        // if the buffer is updated notify the image object
+        if (changed) {
+            evas_object_image_data_update_add(mImage, 0 , 0, buf.width, buf.height);
+        }
+    } else {
+        mPlayer->seek(pos);
+        const std::vector<LOTNode *> &renderList = mPlayer->renderList();
+        update(renderList);
+    }
+}
+
+void LottieView::setFilePath(const char *filePath)
+{
+    mPlayer->setFilePath(filePath);
+}
+
+void LottieView::setSize(int w, int h)
+{
+    if (mRenderMode) {
+        evas_object_resize(mImage, w, h);
+        evas_object_image_size_set(mImage, w, h);
+    } else {
+        evas_object_resize(mVg, w, h);
+    }
+    mPlayer->setSize(w, h);
+}
+void LottieView::setPos(int x, int y)
+{
+    if (mRenderMode) {
+        evas_object_move(mImage, x, y);
+    } else {
+        evas_object_move(mVg, x, y);
+    }
+}
+
+void LottieView::finished()
+{
+    restart();
+}
+
+void LottieView::loop(bool loop)
+{
+    mLoop = loop;
+}
+
+void LottieView::setRepeatCount(int count)
+{
+    mRepeatCount = count;
+}
+
+void LottieView::setRepeatMode(LottieView::RepeatMode mode)
+{
+    mRepeatMode = mode;
+}
+
+void LottieView::play()
+{
+    mAnimator = ecore_animator_timeline_add(mPlayer->playTime()/mSpeed, animator, this);
+    mReverse = false;
+    mCurCount = mRepeatCount;
+    mPalying = true;
+}
+
+void LottieView::pause()
+{
+
+}
+
+void LottieView::stop()
+{
+    mPalying = false;
+}
+
+void LottieView::restart()
+{
+    mCurCount--;
+    if (mLoop || mRepeatCount) {
+        if (mRepeatMode == LottieView::RepeatMode::Reverse)
+            mReverse = !mReverse;
+        else
+            mReverse = false;
+        mAnimator = ecore_animator_timeline_add(mPlayer->playTime()/mSpeed, animator, this);
+    }
+}
diff --git a/example/lottieview.h b/example/lottieview.h
new file mode 100644 (file)
index 0000000..59ea019
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef LOTTIEVIEW_H
+#define LOTTIEVIEW_H
+
+#ifndef EFL_BETA_API_SUPPORT
+#define EFL_BETA_API_SUPPORT
+#endif
+
+#ifndef EFL_EO_API_SUPPORT
+#define EFL_EO_API_SUPPORT
+#endif
+
+#include <Eo.h>
+#include <Efl.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include"lottieplayer.h"
+
+class LOTPlayer;
+class LottieView
+{
+public:
+    enum class RepeatMode {
+        Restart,
+        Reverse
+    };
+    LottieView(Evas *evas, bool renderMode = true);
+    ~LottieView();
+    void setSize(int w, int h);
+    void setPos(int x, int y);
+    void setFilePath(const char *filePath);
+    void show();
+    void hide();
+    void loop(bool loop);
+    void setSpeed(float speed) { mSpeed = speed;}
+    void setRepeatCount(int count);
+    void setRepeatMode(LottieView::RepeatMode mode);
+public:
+    void seek(float pos);
+    void finished();
+    void play();
+    void pause();
+    void stop();
+private:
+    void createVgNode(LOTNode *node, Efl_VG *parent);
+    void update(const std::vector<LOTNode *> &);
+    void restart();
+public:
+    int                      mw;
+    int                      mh;
+    Evas                    *mEvas;
+    Efl_VG                  *mRoot;
+    Evas_Object             *mVg;
+    int                      mRepeatCount;
+    LottieView::RepeatMode   mRepeatMode;
+    LOTPlayer               *mPlayer;
+    Ecore_Animator          *mAnimator;
+    bool                     mLoop;
+    int                      mCurCount;
+    bool                     mReverse;
+    bool                     mPalying;
+    Evas_Object             *mImage;
+    float                    mSpeed;
+    bool                     mRenderMode;
+};
+#endif //LOTTIEVIEW_H
diff --git a/example/lottieviewtest.cpp b/example/lottieviewtest.cpp
new file mode 100644 (file)
index 0000000..169da1e
--- /dev/null
@@ -0,0 +1,123 @@
+#include "evasapp.h"
+#include "lottieview.h"
+#include<iostream>
+#include <dirent.h>
+#include <stdio.h>
+using namespace std;
+
+/*
+ * To check the frame rate with rendermode off run
+ * ECORE_EVAS_FPS_DEBUG=1 ./lottieviewTest --disable-render
+ *
+ * To check the frame rate with  render backend
+ * ECORE_EVAS_FPS_DEBUG=1 ./lottieviewTest
+ *
+ */
+
+class LottieViewTest
+{
+public:
+  LottieViewTest(EvasApp *app, bool renderMode) {
+      mApp = app;
+      mRenderMode = renderMode;
+  }
+
+  bool isJsonFile(const char *filename) {
+    const char *dot = strrchr(filename, '.');
+    if(!dot || dot == filename) return false;
+    return !strcmp(dot + 1, "json");
+  }
+
+  void buildResourceList() {
+      DIR *d;
+      struct dirent *dir;
+      d = opendir(DEMO_DIR);
+      if (d) {
+        while ((dir = readdir(d)) != NULL) {
+          if (isJsonFile(dir->d_name))
+            mResource.push_back(dir->d_name);
+        }
+        closedir(d);
+      }
+  }
+  void displayResourceList() {
+    for(auto i : mResource) {
+       std::string filePath = DEMO_DIR;
+       filePath +=i;
+    }
+  }
+
+  void show() {
+    if (mResource.empty()) return;
+
+    int count = mResource.size();
+    int colums = sqrt(count);
+    int rows = (count % colums) ? colums+1 : colums;
+    int offset = 3;
+    int vw = (mApp->width() - (2 * offset * colums))/colums;
+    int vh = (mApp->height() - (2 * offset * rows))/rows;
+    int posx = offset;
+    int posy = offset;
+    for(auto i : mResource) {
+       std::string filePath = DEMO_DIR;
+       filePath +=i;
+
+       std::unique_ptr<LottieView> view(new LottieView(mApp->evas(), mRenderMode));
+       view->setFilePath(filePath.c_str());
+       view->setPos(posx, posy);
+       view->setSize(vw, vh);
+       view->show();
+       view->play();
+       view->loop(true);
+       //view->setRepeatMode(LottieView::RepeatMode::Reverse);
+
+       posx += vw+offset;
+       if ((mApp->width() - posx) < vw) {
+          posx = offset;
+          posy = posy + vh + offset;
+       }
+       mViews.push_back(std::move(view));
+    }
+
+  }
+
+public:
+  EvasApp     *mApp;
+  bool         mRenderMode = false;
+  std::vector<std::unique_ptr<LottieView>>   mViews;
+  std::vector<std::string>                   mResource;
+};
+
+static void
+onExitCb(void *data)
+{
+    LottieViewTest *view = (LottieViewTest *)data;
+    delete view;
+}
+
+int
+main(int argc, char **argv)
+{
+   EvasApp *app = new EvasApp(800, 800);
+   app->setup();
+
+   bool renderMode = true;
+   if (argc > 1) {
+       if (!strcmp(argv[1],"--disable-render"))
+           renderMode = false;
+   }
+   LottieViewTest *view = new LottieViewTest(app, renderMode);
+   view->buildResourceList();
+   view->show();
+
+   app->addExitCb(onExitCb, view);
+
+   app->run();
+   delete app;
+   return 0;
+}
+
+
+
+
+
diff --git a/example/meson.build b/example/meson.build
new file mode 100644 (file)
index 0000000..6c49d93
--- /dev/null
@@ -0,0 +1,25 @@
+
+common_source  = files('evasapp.cpp')
+common_source += files('lottieview.cpp')
+
+demo_sources = files('demo.cpp')
+demo_sources += common_source
+
+demo_dep = []
+demo_dep+= dependency('elementary')
+
+
+executable('demo',
+           demo_sources,
+           include_directories : inc,
+           link_with : lottie_player_lib,
+           dependencies : demo_dep)
+
+lottieview_test_src  = files('lottieviewtest.cpp')
+lottieview_test_src += common_source
+
+executable('lottieviewTest',
+           lottieview_test_src,
+           include_directories : inc,
+           link_with : lottie_player_lib,
+           dependencies : demo_dep)
diff --git a/example/pathtest.cpp b/example/pathtest.cpp
new file mode 100644 (file)
index 0000000..011d38f
--- /dev/null
@@ -0,0 +1,110 @@
+#include "evasapp.h"
+#include"vpath.h"
+#include<iostream>
+using namespace std;
+
+EvasApp *APP;
+
+static void
+_on_resize(Ecore_Evas *ee)
+{
+   int w, h;
+   ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+   APP->resize(w, h);
+}
+
+class PathTest
+{
+public:
+  PathTest(EvasApp *app) {
+      mApp = app;
+      mShape = evas_vg_shape_add(mApp->root());
+  }
+  void setColor(int r, int g, int b, int a) {
+    evas_vg_node_color_set(mShape, r, g, b, a);
+  }
+
+  void setStrokeColor(int r, int g, int b, int a) {
+    evas_vg_shape_stroke_color_set(mShape, r, g, b, a);
+  }
+
+  void setStrokeWidth(int w) {
+    evas_vg_shape_stroke_width_set(mShape, w);
+  }
+
+  void setPath(const VPath &path) {
+    Efl_VG  *shape = mShape;
+    evas_vg_shape_reset(shape);
+    const std::vector<VPath::Element> &elm = path.elements();
+    const std::vector<VPointF> &pts  = path.points();
+    int i=0;
+    for (auto e : elm) {
+      switch(e) {
+        case VPath::Element::MoveTo:
+            {
+                VPointF p = pts[i++];
+                evas_vg_shape_append_move_to(shape, p.x(), p.y());
+                break;
+            }
+        case VPath::Element::LineTo:
+            {
+                VPointF p = pts[i++];
+                evas_vg_shape_append_line_to(shape, p.x(), p.y());
+                break;
+            }
+        case VPath::Element::CubicTo:
+            {
+                VPointF p = pts[i++];
+                VPointF p1 = pts[i++];
+                VPointF p2 = pts[i++];
+                evas_vg_shape_append_cubic_to(shape, p.x(), p.y(), p1.x(), p1.y(), p2.x(), p2.y());
+                break;
+            }
+        case VPath::Element::Close:
+            {
+                evas_vg_shape_append_close(shape);
+                break;
+            }
+      }
+    }
+  }
+
+public:
+  EvasApp *mApp;
+  Efl_VG  *mShape;
+};
+
+int
+main(void)
+{
+   APP = new EvasApp(800, 800);
+   ecore_evas_callback_resize_set(APP->mEcoreEvas, _on_resize);
+   APP->setup();
+
+   VPath path;
+   path.addRoundRect(VRectF(100, 100, 200, 200), 20, 20, VPath::Direction::CCW);
+   path.addCircle(50, 50, 20, VPath::Direction::CCW);
+
+   path.addOval(VRectF(300, 100, 100, 50), VPath::Direction::CCW);
+
+   path.addPolystarStar(0.0, 150, 150, 15.0,
+                        106.0, 34.0,
+                        231.0, 88.0, VPath::Direction::CW);
+
+
+   PathTest test(APP);
+   test.setPath(path);
+   test.setColor(255, 0, 0, 255);
+   test.setStrokeColor(200, 200, 0, 200);
+   test.setStrokeWidth(5);
+
+
+   APP->run();
+   delete APP;
+   return 0;
+}
+
+
+
+
+
diff --git a/example/resource/acrobatics.json b/example/resource/acrobatics.json
new file mode 100644 (file)
index 0000000..7dce062
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.5","fr":29.9700012207031,"ip":0,"op":46.0000018736184,"w":288,"h":360,"nm":"jump","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"a7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[29],"e":[-23]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20,"s":[-23],"e":[-52]},{"t":30.0000012219251}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.585,"y":0.651},"o":{"x":0.232,"y":0.316},"n":"0p585_0p651_0p232_0p316","t":0,"s":[223.86,137.848,0],"e":[173.574,30.76,0],"to":[-1.73460388183594,-57.7228088378906,0],"ti":[29.553560256958,21.1779880523682,0]},{"i":{"x":0.649,"y":0.681},"o":{"x":0.307,"y":0.343},"n":"0p649_0p681_0p307_0p343","t":5,"s":[173.574,30.76,0],"e":[85.663,23.367,0],"to":[-27.3250160217285,-19.5810222625732,0],"ti":[26.9960441589355,-14.1411228179932,0]},{"i":{"x":0.671,"y":0.846},"o":{"x":0.331,"y":0.195},"n":"0p671_0p846_0p331_0p195","t":10,"s":[85.663,23.367,0],"e":[36.511,149.665,0],"to":[-19.3489608764648,10.1354112625122,0],"ti":[-6.15889406204224,-86.5106048583984,0]},{"i":{"x":0.696,"y":0.889},"o":{"x":0.354,"y":0.179},"n":"0p696_0p889_0p354_0p179","t":15,"s":[36.511,149.665,0],"e":[74.554,29.842,0],"to":[1.17162692546844,16.4571990966797,0],"ti":[-42.0285682678223,27.0350360870361,0]},{"i":{"x":0.652,"y":0.943},"o":{"x":0.268,"y":0.138},"n":"0p652_0p943_0p268_0p138","t":20,"s":[74.554,29.842,0],"e":[163.85,25.327,0],"to":[50.7120628356934,-30.1498985290527,0],"ti":[-6.47687864303589,-3.83117437362671,0]},{"i":{"x":0.855,"y":0.672},"o":{"x":0.451,"y":0.05},"n":"0p855_0p672_0p451_0p05","t":25,"s":[163.85,25.327,0],"e":[223.441,143.403,0],"to":[73.0731658935547,50.364128112793,0],"ti":[-1.62066376209259,-0.90557879209518,0]},{"i":{"x":0.585,"y":0.656},"o":{"x":0.262,"y":0.658},"n":"0p585_0p656_0p262_0p658","t":30,"s":[223.441,143.403,0],"e":[173.574,30.76,0],"to":[1.62066376209259,0.90557879209518,0],"ti":[29.553560256958,21.1779880523682,0]},{"i":{"x":0.649,"y":0.681},"o":{"x":0.307,"y":0.343},"n":"0p649_0p681_0p307_0p343","t":35,"s":[173.574,30.76,0],"e":[85.663,23.367,0],"to":[-27.3250160217285,-19.5810222625732,0],"ti":[26.9960441589355,-14.1411228179932,0]},{"i":{"x":0.671,"y":0.75},"o":{"x":0.331,"y":0.315},"n":"0p671_0p75_0p331_0p315","t":40,"s":[85.663,23.367,0],"e":[63.011,118.165,0],"to":[-19.3489608764648,10.1354112625122,0],"ti":[-21.1588935852051,-45.0106010437012,0]},{"t":46.0000018736184}],"ix":2},"a":{"a":0,"k":[49.985,49.973,0],"ix":1},"s":{"a":0,"k":[-50.014,50.014,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.5,-8.4],[8.4,1.6],[0.199,0.1],[2.599,-0.9],[0.201,0],[1.6,8.5],[-8.5,1.6],[-2.8,-1.2],[0,0],[0,0],[2.9,2.9],[0,0],[-4.2,-4.1],[-0.2,-2.1],[0,0],[-0.5,-0.3],[0.299,-0.5],[0,0],[-0.7,0.3],[-3.2,-0.6]],"o":[[-1.6,8.5],[-0.3,0],[-2.5,-0.9],[-0.201,0.1],[-8.5,1.6],[-1.6,-8.5],[3.2,-0.6],[1,0.5],[0,0],[-2,0],[-4.199,-4.2],[0,0],[2.3,2.3],[0,0],[0.3,-0.5],[0.5,0.3],[0,0],[0.801,-0.1],[2.8,-1.3],[8.5,1.5]],"v":[[25.65,1.6],[4.65,29.5],[3.951,29.3],[-3.749,29.3],[-4.45,29.5],[-25.55,1.6],[-13.05,-16.6],[-3.85,-15.5],[-0.749,-14.7],[0.45,-16.9],[-8.05,-20.3],[-11.35,-30.3],[-1.35,-27],[1.951,-19.7],[7.451,-29.9],[8.85,-30.3],[9.251,-28.9],[1.65,-14.7],[3.85,-15.4],[13.05,-16.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.380392163992,0.411764711142,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50.366,50.503],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":167.000006802049,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"a6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":29,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.67},"o":{"x":0.185,"y":0.366},"n":"0p833_0p67_0p185_0p366","t":0,"s":[186.985,40.473,0],"e":[216.985,116.473,0],"to":[1.01539611816406,-0.97280883789062,0],"ti":[6.98460388183594,-38.0271911621094,0]},{"i":{"x":0.833,"y":0.74},"o":{"x":0.185,"y":0.289},"n":"0p833_0p74_0p185_0p289","t":5,"s":[216.985,116.473,0],"e":[169.985,27.473,0],"to":[5.01539611816406,-38.4728088378906,0],"ti":[28.5490169525146,10.817400932312,0]},{"i":{"x":0.833,"y":0.716},"o":{"x":0.185,"y":0.315},"n":"0p833_0p716_0p185_0p315","t":10,"s":[169.985,27.473,0],"e":[76.485,28.973,0],"to":[-64,-24.25,0],"ti":[8.20706558227539,-7.81933689117432,0]},{"i":{"x":0.833,"y":0.732},"o":{"x":0.185,"y":0.298},"n":"0p833_0p732_0p185_0p298","t":15,"s":[76.485,28.973,0],"e":[42.485,121.973,0],"to":[-36.8030128479004,35.0643157958984,0],"ti":[-8.51539611816406,-3.02719116210938,0]},{"i":{"x":0.833,"y":0.756},"o":{"x":0.185,"y":0.271},"n":"0p833_0p756_0p185_0p271","t":20,"s":[42.485,121.973,0],"e":[85.485,24.473,0],"to":[-12.9846038818359,-63.4728088378906,0],"ti":[-12.5153961181641,8.47280883789062,0]},{"i":{"x":0.833,"y":0.717},"o":{"x":0.185,"y":0.314},"n":"0p833_0p717_0p185_0p314","t":25,"s":[85.485,24.473,0],"e":[178.485,31.973,0],"to":[40.0153961181641,-28.4728088378906,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.775},"o":{"x":0.167,"y":0.225},"n":"0p833_0p775_0p167_0p225","t":30,"s":[178.485,31.973,0],"e":[216.485,144.473,0],"to":[0,0,0],"ti":[16.4846038818359,-63.0271911621094,0]},{"i":{"x":0.833,"y":0.792},"o":{"x":0.185,"y":0.23},"n":"0p833_0p792_0p185_0p23","t":35,"s":[216.485,144.473,0],"e":[169.985,27.473,0],"to":[12.0153961181641,-51.9728088378906,0],"ti":[28.5490169525146,10.817400932312,0]},{"i":{"x":0.833,"y":0.669},"o":{"x":0.185,"y":0.368},"n":"0p833_0p669_0p185_0p368","t":40,"s":[169.985,27.473,0],"e":[73.485,29.473,0],"to":[-64,-24.25,0],"ti":[25.5490169525146,-10.682599067688,0]},{"t":46.0000018736184}],"ix":2},"a":{"a":0,"k":[49.985,49.973,0],"ix":1},"s":{"a":0,"k":[-50.014,50.014,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.5,-8.4],[8.4,1.6],[0.199,0.1],[2.599,-0.9],[0.201,0],[1.6,8.5],[-8.5,1.6],[-2.8,-1.2],[0,0],[0,0],[2.9,2.9],[0,0],[-4.2,-4.1],[-0.2,-2.1],[0,0],[-0.5,-0.3],[0.299,-0.5],[0,0],[-0.7,0.3],[-3.2,-0.6]],"o":[[-1.6,8.5],[-0.3,0],[-2.5,-0.9],[-0.201,0.1],[-8.5,1.6],[-1.6,-8.5],[3.2,-0.6],[1,0.5],[0,0],[-2,0],[-4.199,-4.2],[0,0],[2.3,2.3],[0,0],[0.3,-0.5],[0.5,0.3],[0,0],[0.801,-0.1],[2.8,-1.3],[8.5,1.5]],"v":[[25.65,1.6],[4.65,29.5],[3.951,29.3],[-3.749,29.3],[-4.45,29.5],[-25.55,1.6],[-13.05,-16.6],[-3.85,-15.5],[-0.749,-14.7],[0.45,-16.9],[-8.05,-20.3],[-11.35,-30.3],[-1.35,-27],[1.951,-19.7],[7.451,-29.9],[8.85,-30.3],[9.251,-28.9],[1.65,-14.7],[3.85,-15.4],[13.05,-16.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.380392163992,0.411764711142,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50.366,50.503],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":167.000006802049,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"a5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":29,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.585,"y":0.55},"o":{"x":0.185,"y":0.325},"n":"0p585_0p55_0p185_0p325","t":0,"s":[82.985,26.973,0],"e":[173.574,30.76,0],"to":[0.01539611816406,-3.97280883789062,0],"ti":[-43.4464416503906,-27.3220119476318,0]},{"i":{"x":0.585,"y":0.412},"o":{"x":0.303,"y":0.429},"n":"0p585_0p412_0p303_0p429","t":5,"s":[173.574,30.76,0],"e":[205.574,91.76,0],"to":[23.8329467773438,26.0342674255371,0],"ti":[6.07351684570312,9.26034545898438,0]},{"i":{"x":0.585,"y":0.449},"o":{"x":0.303,"y":0.402},"n":"0p585_0p449_0p303_0p402","t":10,"s":[205.574,91.76,0],"e":[167.074,25.76,0],"to":[-9.07351684570312,-39.7603454589844,0],"ti":[15.0735168457031,13.2603454589844,0]},{"i":{"x":0.585,"y":0.526},"o":{"x":0.303,"y":0.345},"n":"0p585_0p526_0p303_0p345","t":15,"s":[167.074,25.76,0],"e":[80.574,28.26,0],"to":[-48.5735168457031,-23.5103454589844,0],"ti":[8.82351684570312,-4.48965454101562,0]},{"i":{"x":0.585,"y":0.478},"o":{"x":0.303,"y":0.381},"n":"0p585_0p478_0p303_0p381","t":20,"s":[80.574,28.26,0],"e":[55.074,104.26,0],"to":[-26.8235168457031,30.9896545410156,0],"ti":[-2.42648315429688,-0.48965454101562,0]},{"i":{"x":0.585,"y":0.489},"o":{"x":0.303,"y":0.372},"n":"0p585_0p489_0p303_0p372","t":25,"s":[55.074,104.26,0],"e":[83.574,27.76,0],"to":[1.17648315429688,-59.2603454589844,0],"ti":[0,0,0]},{"i":{"x":0.585,"y":0.542},"o":{"x":0.167,"y":0.184},"n":"0p585_0p542_0p167_0p184","t":30,"s":[83.574,27.76,0],"e":[173.574,30.76,0],"to":[0,0,0],"ti":[-43.4464416503906,-27.3220119476318,0]},{"i":{"x":0.585,"y":0.624},"o":{"x":0.303,"y":0.274},"n":"0p585_0p624_0p303_0p274","t":35,"s":[173.574,30.76,0],"e":[217.074,133.26,0],"to":[23.8329467773438,26.0342674255371,0],"ti":[6.07351684570312,9.26034545898438,0]},{"i":{"x":0.585,"y":0.567},"o":{"x":0.303,"y":0.316},"n":"0p585_0p567_0p303_0p316","t":40,"s":[217.074,133.26,0],"e":[168.574,25.76,0],"to":[-17.0735168457031,-77.0103454589844,0],"ti":[6.07351684570312,9.26034545898438,0]},{"t":46.0000018736184}],"ix":2},"a":{"a":0,"k":[49.985,49.973,0],"ix":1},"s":{"a":0,"k":[-50.014,50.014,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.5,-8.4],[8.4,1.6],[0.199,0.1],[2.599,-0.9],[0.201,0],[1.6,8.5],[-8.5,1.6],[-2.8,-1.2],[0,0],[0,0],[2.9,2.9],[0,0],[-4.2,-4.1],[-0.2,-2.1],[0,0],[-0.5,-0.3],[0.299,-0.5],[0,0],[-0.7,0.3],[-3.2,-0.6]],"o":[[-1.6,8.5],[-0.3,0],[-2.5,-0.9],[-0.201,0.1],[-8.5,1.6],[-1.6,-8.5],[3.2,-0.6],[1,0.5],[0,0],[-2,0],[-4.199,-4.2],[0,0],[2.3,2.3],[0,0],[0.3,-0.5],[0.5,0.3],[0,0],[0.801,-0.1],[2.8,-1.3],[8.5,1.5]],"v":[[25.65,1.6],[4.65,29.5],[3.951,29.3],[-3.749,29.3],[-4.45,29.5],[-25.55,1.6],[-13.05,-16.6],[-3.85,-15.5],[-0.749,-14.7],[0.45,-16.9],[-8.05,-20.3],[-11.35,-30.3],[-1.35,-27],[1.951,-19.7],[7.451,-29.9],[8.85,-30.3],[9.251,-28.9],[1.65,-14.7],[3.85,-15.4],[13.05,-16.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.986887276173,0.380120933056,0.411817699671,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50.125,50.785],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":167.000006802049,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"a1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":29,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.732},"o":{"x":0.185,"y":0.298},"n":"0p833_0p732_0p185_0p298","t":0,"s":[59.985,118.973,0],"e":[82.985,26.973,0],"to":[-19.4846038818359,-32.9728088378906,0],"ti":[-31.0153961181641,26.4728088378906,0]},{"i":{"x":0.833,"y":0.675},"o":{"x":0.185,"y":0.361},"n":"0p833_0p675_0p185_0p361","t":5,"s":[82.985,26.973,0],"e":[162.985,24.473,0],"to":[31.0153961181641,-26.4728088378906,0],"ti":[-21.0153961181641,-9.52719116210938,0]},{"i":{"x":0.833,"y":0.572},"o":{"x":0.185,"y":0.475},"n":"0p833_0p572_0p185_0p475","t":10,"s":[162.985,24.473,0],"e":[203.985,72.973,0],"to":[21.0153961181641,9.52719116210938,0],"ti":[-4.51539611816406,-16.5271911621094,0]},{"i":{"x":0.833,"y":0.556},"o":{"x":0.185,"y":0.493},"n":"0p833_0p556_0p185_0p493","t":15,"s":[203.985,72.973,0],"e":[165.485,24.973,0],"to":[-10.9846038818359,-31.4728088378906,0],"ti":[7.73460388183594,4.09780883789062,0]},{"i":{"x":0.833,"y":0.67},"o":{"x":0.185,"y":0.366},"n":"0p833_0p67_0p185_0p366","t":20,"s":[165.485,24.973,0],"e":[85.985,24.223,0],"to":[-51.1335983276367,-27.8870468139648,0],"ti":[2.48460388183594,-2.02719116210938,0]},{"i":{"x":0.833,"y":0.702},"o":{"x":0.185,"y":0.331},"n":"0p833_0p702_0p185_0p331","t":25,"s":[85.985,24.223,0],"e":[53.485,105.723,0],"to":[-50.724681854248,41.3863258361816,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.677},"o":{"x":0.167,"y":0.323},"n":"0p833_0p677_0p167_0p323","t":30,"s":[53.485,105.723,0],"e":[82.985,26.973,0],"to":[0,0,0],"ti":[-31.0153961181641,26.4728088378906,0]},{"i":{"x":0.833,"y":0.675},"o":{"x":0.185,"y":0.361},"n":"0p833_0p675_0p185_0p361","t":35,"s":[82.985,26.973,0],"e":[162.985,24.473,0],"to":[31.0153961181641,-26.4728088378906,0],"ti":[-21.0153961181641,-9.52719116210938,0]},{"i":{"x":0.833,"y":0.753},"o":{"x":0.185,"y":0.274},"n":"0p833_0p753_0p185_0p274","t":40,"s":[162.985,24.473,0],"e":[219.985,140.473,0],"to":[21.0153961181641,9.52719116210938,0],"ti":[-15.0153961181641,-99.0271911621094,0]},{"t":46.0000018736184}],"ix":2},"a":{"a":0,"k":[49.985,49.973,0],"ix":1},"s":{"a":0,"k":[-50.014,50.014,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.5,-8.4],[8.4,1.6],[0.199,0.1],[2.599,-0.9],[0.201,0],[1.6,8.5],[-8.5,1.6],[-2.8,-1.2],[0,0],[0,0],[2.9,2.9],[0,0],[-4.2,-4.1],[-0.2,-2.1],[0,0],[-0.5,-0.3],[0.299,-0.5],[0,0],[-0.7,0.3],[-3.2,-0.6]],"o":[[-1.6,8.5],[-0.3,0],[-2.5,-0.9],[-0.201,0.1],[-8.5,1.6],[-1.6,-8.5],[3.2,-0.6],[1,0.5],[0,0],[-2,0],[-4.199,-4.2],[0,0],[2.3,2.3],[0,0],[0.3,-0.5],[0.5,0.3],[0,0],[0.801,-0.1],[2.8,-1.3],[8.5,1.5]],"v":[[25.65,1.6],[4.65,29.5],[3.951,29.3],[-3.749,29.3],[-4.45,29.5],[-25.55,1.6],[-13.05,-16.6],[-3.85,-15.5],[-0.749,-14.7],[0.45,-16.9],[-8.05,-20.3],[-11.35,-30.3],[-1.35,-27],[1.951,-19.7],[7.451,-29.9],[8.85,-30.3],[9.251,-28.9],[1.65,-14.7],[3.85,-15.4],[13.05,-16.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.380392163992,0.411764711142,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50.366,50.503],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":167.000006802049,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"teeth Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[20.312,41.66,0],"ix":2},"a":{"a":0,"k":[3.718,3.618,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-0.646,-2.841],[2.087,-2.018],[-0.571,2.841],[-2.087,2.392]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[5.1,4.146],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.338,-2.982],[1.673,-2.18],[-0.039,2.982],[-1.673,2.673]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.923,3.232],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"right eye Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[42.538,16.724,0],"e":[47.764,16.011,0],"to":[0.87093943357468,-0.11872462183237,0],"ti":[-0.05034303665161,-1.83084356784821,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[47.764,16.011,0],"e":[41.775,15.029,0],"to":[0.01295710168779,0.4712156355381,0],"ti":[3.24095487594604,-0.21771761775017,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[41.775,15.029,0],"e":[35.94,19.224,0],"to":[-2.2777636051178,0.15301331877708,0],"ti":[0.64945775270462,-2.75669002532959,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":19,"s":[35.94,19.224,0],"e":[37.155,29.03,0],"to":[-1.57354974746704,6.67909383773804,0],"ti":[0.77238988876343,-1.01815032958984,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[37.155,29.03,0],"e":[38.689,17.956,0],"to":[-0.66211730241776,0.87279099225998,0],"ti":[-4.08277225494385,3.6608738899231,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":30.5,"s":[38.689,17.956,0],"e":[46.673,19.389,0],"to":[4.08277225494385,-3.6608738899231,0],"ti":[0,0,0]},{"t":42.0000017106951}],"ix":2},"a":{"a":0,"k":[2.909,2.909,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[110,110,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":17,"s":[100,100,100],"e":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":22,"s":[110,110,100],"e":[100,100,100]},{"t":42.0000017106951}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.86,-0.994],[0.994,-0.859],[0.86,0.994],[-0.994,0.86]],"o":[[0.86,0.994],[-0.993,0.86],[-0.86,-0.993],[0.993,-0.859]],"v":[[1.799,-1.557],[1.556,1.798],[-1.799,1.556],[-1.556,-1.799]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.349000010771,0.349000010771,0.349000010771,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.909,2.909],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"left eye Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[20.352,12.353,0],"e":[23.754,15.218,0],"to":[0.75799149274826,-0.13729524612427,0],"ti":[1.20316421985626,-1.69811820983887,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[23.754,15.218,0],"e":[14.162,14.634,0],"to":[-0.43755486607552,0.61755484342575,0],"ti":[5.72465705871582,-5.02380609512329,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18.75,"s":[14.162,14.634,0],"e":[14.48,23.107,0],"to":[-5.72465705871582,5.02380609512329,0],"ti":[1.21317493915558,0.42704656720161,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[14.48,23.107,0],"e":[14.053,13.997,0],"to":[-1.21317493915558,-0.42704656720161,0],"ti":[-4.91244649887085,5.34288120269775,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":29.75,"s":[14.053,13.997,0],"e":[23.538,15.395,0],"to":[4.91244649887085,-5.34288120269775,0],"ti":[-1.43252968788147,0.25947457551956,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[23.538,15.395,0],"e":[20.161,13.089,0],"to":[1.894491314888,-0.34314984083176,0],"ti":[-1.51578795909882,0.27455517649651,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[2.909,2.909,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[110,110,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":17,"s":[100,100,100],"e":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":22,"s":[110,110,100],"e":[100,100,100]},{"t":42.0000017106951}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.86,-0.994],[0.994,-0.859],[0.86,0.993],[-0.994,0.86]],"o":[[0.86,0.994],[-0.993,0.86],[-0.86,-0.993],[0.993,-0.859]],"v":[[1.799,-1.557],[1.556,1.798],[-1.799,1.556],[-1.556,-1.799]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.349000010771,0.349000010771,0.349000010771,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.909,2.909],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"right blush Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[9.908,34.526,0],"ix":2},"a":{"a":0,"k":[4.844,4.952,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.811,2.618],[-2.549,-0.789],[0.811,-2.618],[2.549,0.79]],"o":[[0.811,-2.618],[2.548,0.79],[-0.811,2.619],[-2.548,-0.789]],"v":[[-4.615,-1.43],[1.469,-4.742],[4.615,1.428],[-1.469,4.739]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.976000019148,0.430999995213,0.430999995213,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.844,4.952],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"left blush Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[37.356,40.818,0],"ix":2},"a":{"a":0,"k":[5.879,5.991,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.756,-1.649],[1.716,2.869],[-2.756,1.649],[-1.716,-2.868]],"o":[[-2.756,1.649],[-1.716,-2.868],[2.756,-1.649],[1.716,2.868]],"v":[[3.107,5.193],[-4.99,2.986],[-3.107,-5.192],[4.99,-2.985]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.976000019148,0.430999995213,0.430999995213,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[5.879,5.991],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"glass leg Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-42]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-42],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[-42]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[-42],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[58.822,31.197,0],"e":[64.21,21.554,0],"to":[0.89794141054153,-1.60704386234283,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[64.21,21.554,0],"e":[58.822,31.197,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[58.822,31.197,0],"e":[64.21,21.554,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[64.21,21.554,0],"e":[58.822,31.197,0],"to":[0,0,0],"ti":[0.89794141054153,-1.60704386234283,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[10.014,11.241,0],"ix":1},"s":{"a":0,"k":[104.675,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[4.892,8.892],[7.945,5.839],[-4.892,-8.892]],"c":true}],"e":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[1.191,4.186],[4.147,0.904],[-4.892,-8.892]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[1.191,4.186],[4.147,0.904],[-4.892,-8.892]],"c":true}],"e":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[4.892,8.892],[7.945,5.839],[-4.892,-8.892]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[4.892,8.892],[7.945,5.839],[-4.892,-8.892]],"c":true}],"e":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[1.191,4.186],[4.147,0.904],[-4.892,-8.892]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[1.191,4.186],[4.147,0.904],[-4.892,-8.892]],"c":true}],"e":[{"i":[[-1.819,-2.087],[-4.279,-4.91],[1.819,2.088],[4.279,4.91]],"o":[[4.278,4.91],[1.828,2.099],[-4.28,-4.91],[-1.829,-2.099]],"v":[[-7.945,-5.839],[4.892,8.892],[7.945,5.839],[-4.892,-8.892]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.313999998803,0.365000017952,0.976000019148,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[10.014,11.241],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"glass mid","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[31.189,19.107,0],"ix":2},"a":{"a":0,"k":[12.089,10.362,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-3.454,-3.454]],"o":[[0,0],[0,0]],"v":[[-3.454,0],[3.454,1.727]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.313999998803,0.365000017952,0.976000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.454,"ix":5},"lc":1,"lj":1,"ml":10,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12.089,10.362],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"right glass Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[9]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[9],"e":[0]},{"t":22.0000008960784}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[42.247,23.795,0],"e":[42.247,19.795,0],"to":[0,-0.66666668653488,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[42.247,19.795,0],"e":[42.247,23.795,0],"to":[0,0,0],"ti":[0,-0.66666668653488,0]},{"t":22.0000008960784}],"ix":2},"a":{"a":0,"k":[18.398,18.804,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.726,-1.893],[3.751,3.273],[-3.663,4.242],[-3.679,-3.355],[-0.074,-2.34]],"o":[[-3.452,3.786],[-3.725,-3.252],[3.349,-3.877],[1.839,1.677],[0.074,2.339]],"v":[[7.221,6.114],[-5.691,6.896],[-6.101,-6.033],[6.811,-6.814],[9.689,-0.512]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.313999998803,0.365000017952,0.976000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.454,"ix":5},"lc":1,"lj":1,"ml":10,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[18.398,18.804],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"right ear Outlines","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-14],"e":[-14]},{"t":42.0000017106951}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[68.438,22.11,0],"e":[59.029,9.437,0],"to":[-1.56817352771759,-2.11210107803345,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[59.029,9.437,0],"e":[68.438,22.11,0],"to":[0,0,0],"ti":[-0.69228959083557,-0.1520429700613,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[68.438,22.11,0],"e":[63.183,10.349,0],"to":[0.69228959083557,0.1520429700613,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[63.183,10.349,0],"e":[68.438,22.11,0],"to":[0,0,0],"ti":[-0.87588393688202,-1.96005809307098,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[7.277,8.167,0],"ix":1},"s":{"a":0,"k":[87.794,95.636,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.205,"y":1},"o":{"x":1,"y":0},"n":"0p205_1_1_0","t":0,"s":[{"i":[[0.373,-0.494],[-4.647,-2.786],[-0.182,3.144],[3.477,0]],"o":[[-2.483,3.287],[1.628,0.976],[0.235,-4.094],[-1.392,0]],"v":[[-4.544,-7.153],[2.088,4.966],[8.16,-3.175],[-1.665,-7.917]],"c":true}],"e":[{"i":[[0.373,-0.494],[-4.647,-2.786],[-0.182,3.144],[3.477,0]],"o":[[-2.483,3.287],[1.628,0.976],[0.235,-4.094],[-1.392,0]],"v":[[-4.544,-7.153],[2.088,4.966],[8.16,-3.175],[-1.665,-7.917]],"c":true}]},{"i":{"x":1,"y":0.998},"o":{"x":1,"y":0},"n":"1_0p998_1_0","t":18,"s":[{"i":[[0.373,-0.494],[-4.647,-2.786],[-0.182,3.144],[3.477,0]],"o":[[-2.483,3.287],[1.628,0.976],[0.235,-4.094],[-1.392,0]],"v":[[-4.544,-7.153],[2.088,4.966],[8.16,-3.175],[-1.665,-7.917]],"c":true}],"e":[{"i":[[0.373,-0.494],[-4.647,-2.786],[-0.182,3.144],[3.477,0]],"o":[[-2.483,3.287],[1.628,0.976],[0.235,-4.094],[-1.392,0]],"v":[[-4.544,-7.153],[2.088,4.966],[8.16,-3.175],[-1.665,-7.917]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.052,"y":1},"n":"0_1_0p052_1","t":23,"s":[{"i":[[0.373,-0.494],[-4.647,-2.786],[-0.182,3.144],[3.477,0]],"o":[[-2.483,3.287],[1.628,0.976],[0.235,-4.094],[-1.392,0]],"v":[[-4.544,-7.153],[2.088,4.966],[8.16,-3.175],[-1.665,-7.917]],"c":true}],"e":[{"i":[[0.373,-0.494],[-4.647,-2.786],[-0.182,3.144],[3.477,0]],"o":[[-2.483,3.287],[1.628,0.976],[0.235,-4.094],[-1.392,0]],"v":[[-4.544,-7.153],[2.088,4.966],[8.16,-3.175],[-1.665,-7.917]],"c":true}]},{"t":34.0000013848484}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0.115,0.286],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[99.454,101.055],"ix":3},"r":{"a":0,"k":-1.33,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.984313726425,0.525490224361,0.537254929543,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[7.541,7.827],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[104.62,100.21],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"left glass Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-24]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-24],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[-3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[-3],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[113.337,79.663,0],"e":[86.337,67.663,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[86.337,67.663,0],"e":[113.337,79.663,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[113.337,79.663,0],"e":[105.337,60.663,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[105.337,60.663,0],"e":[113.337,79.663,0],"to":[0,0,0],"ti":[0,0,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[18.693,18.548,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[4.149,-2.624],[2.546,4.026],[-4.149,2.623],[-2.546,-4.025]],"o":[[-4.149,2.624],[-2.546,-4.025],[4.149,-2.624],[2.546,4.026]],"v":[[4.611,7.289],[-7.512,4.751],[-4.609,-7.29],[7.512,-4.752]],"c":true}],"e":[{"i":[[4.149,-2.624],[2.546,4.026],[-4.149,2.623],[-2.546,-4.025]],"o":[[-4.149,2.624],[-2.546,-4.025],[4.149,-2.624],[2.546,4.026]],"v":[[4.611,7.289],[-7.512,4.751],[-4.609,-7.29],[7.512,-4.752]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.313999998803,0.365000017952,0.976000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.454,"ix":5},"lc":1,"lj":1,"ml":10,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[18.693,18.548],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"right leg Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-21]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-21],"e":[2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[2],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[137.234,263.001,0],"e":[149.234,229.001,0],"to":[2,-5.66666650772095,0],"ti":[-1.5,-0.5,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[149.234,229.001,0],"e":[146.234,266.001,0],"to":[1.5,0.5,0],"ti":[3.5,-0.16666667163372,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[146.234,266.001,0],"e":[128.234,230.001,0],"to":[-3.5,0.16666667163372,0],"ti":[1.5,0.5,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[128.234,230.001,0],"e":[137.234,263.001,0],"to":[-1.5,-0.5,0],"ti":[-1.5,-5.5,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[6.404,11.75,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[1.203,-6.24],[-7.402,0.838],[1.536,2.127],[0,0]],"o":[[-1.039,5.389],[4.747,-0.538],[0,0],[0,0]],"v":[[-10.659,2.429],[-9.691,18.566],[16.208,-23.499],[-4.812,-19.381]],"c":true}],"e":[{"i":[[3.453,-0.562],[-9.391,2.685],[1.536,2.127],[0,0]],"o":[[-5.722,0.931],[9.444,-2.7],[0,0],[0,0]],"v":[[-8.909,-9.07],[-7.939,-2.692],[7.715,-15.478],[-4.811,-19.381]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[3.453,-0.562],[-9.391,2.685],[1.536,2.127],[0,0]],"o":[[-5.722,0.931],[9.444,-2.7],[0,0],[0,0]],"v":[[-8.909,-9.07],[-7.939,-2.692],[7.715,-15.478],[-4.811,-19.381]],"c":true}],"e":[{"i":[[3.453,-0.562],[-9.391,2.685],[1.536,2.127],[0,0]],"o":[[-5.722,0.931],[9.444,-2.7],[0,0],[0,0]],"v":[[-8.909,-9.07],[-7.939,-2.692],[7.715,-15.478],[-4.811,-19.381]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[3.453,-0.562],[-9.391,2.685],[1.536,2.127],[0,0]],"o":[[-5.722,0.931],[9.444,-2.7],[0,0],[0,0]],"v":[[-8.909,-9.07],[-7.939,-2.692],[7.715,-15.478],[-4.811,-19.381]],"c":true}],"e":[{"i":[[3.453,-0.562],[-9.391,2.685],[1.536,2.127],[0,0]],"o":[[-5.722,0.931],[9.444,-2.7],[0,0],[0,0]],"v":[[-8.909,-9.07],[-7.939,-2.692],[7.715,-15.478],[-4.811,-19.381]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[3.453,-0.562],[-9.391,2.685],[1.536,2.127],[0,0]],"o":[[-5.722,0.931],[9.444,-2.7],[0,0],[0,0]],"v":[[-8.909,-9.07],[-7.939,-2.692],[7.715,-15.478],[-4.811,-19.381]],"c":true}],"e":[{"i":[[1.203,-6.24],[-7.402,0.838],[1.536,2.127],[0,0]],"o":[[-1.039,5.389],[4.747,-0.538],[0,0],[0,0]],"v":[[-10.659,2.43],[-11.187,28.112],[16.208,-23.499],[-4.811,-19.381]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,99.949],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.976470589638,0.57647061348,0.57647061348,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[9.625,19.807],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.231,99.569],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"left leg Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-7]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-7],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[5],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[114.346,258.893,0],"e":[119.596,226.893,0],"to":[0.875,-5.33333349227905,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[119.596,226.893,0],"e":[119.846,266.393,0],"to":[0,0,0],"ti":[0.46127620339394,-2.81158828735352,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[119.846,266.393,0],"e":[97.596,219.893,0],"to":[-0.875,5.33333349227905,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[97.596,219.893,0],"e":[114.346,258.893,0],"to":[0,0,0],"ti":[-2.79166674613953,-6.5,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[16.021,16.585,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-12.369,-1.388],[2.77,3.145],[0,1.393],[0,0],[0,0]],"o":[[10.544,1.183],[-1.665,-1.89],[0,-7.65],[0,0],[0,0]],"v":[[4.143,8.543],[10.207,3.82],[6.493,-1.492],[0.929,-14.48],[-15.771,-16.335]],"c":true}],"e":[{"i":[[-11.149,1.063],[4.196,6.676],[0,0],[0,0],[0,0]],"o":[[4.05,-0.386],[-2.99,-4.757],[0,0],[0,0],[0,0]],"v":[[9.806,8.474],[5.06,-1.478],[2.993,-11.242],[0.928,-14.48],[-16.343,-15.672]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[-11.149,1.063],[4.196,6.676],[0,0],[0,0],[0,0]],"o":[[4.05,-0.386],[-2.99,-4.757],[0,0],[0,0],[0,0]],"v":[[9.806,8.474],[5.06,-1.478],[2.993,-11.242],[0.928,-14.48],[-16.343,-15.672]],"c":true}],"e":[{"i":[[-9.87,-7.218],[0.816,5.06],[0,1.393],[0,0],[0,0]],"o":[[5.454,3.988],[-0.463,-2.868],[0,-7.65],[0,0],[0,0]],"v":[[8.992,22.67],[9.912,7.963],[6.01,-5.865],[0.929,-14.48],[-19.64,-21.193]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[-9.87,-7.218],[0.816,5.06],[0,1.393],[0,0],[0,0]],"o":[[5.454,3.988],[-0.463,-2.868],[0,-7.65],[0,0],[0,0]],"v":[[8.992,22.67],[9.912,7.963],[6.01,-5.865],[0.929,-14.48],[-19.64,-21.193]],"c":true}],"e":[{"i":[[-11.149,1.063],[4.196,6.676],[0,0],[0,0],[0,0]],"o":[[4.05,-0.386],[-2.99,-4.757],[0,0],[0,0],[0,0]],"v":[[9.806,8.474],[5.06,-1.478],[2.993,-11.242],[0.928,-14.48],[-16.343,-15.672]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[-11.149,1.063],[4.196,6.676],[0,0],[0,0],[0,0]],"o":[[4.05,-0.386],[-2.99,-4.757],[0,0],[0,0],[0,0]],"v":[[9.806,8.474],[5.06,-1.478],[2.993,-11.242],[0.928,-14.48],[-16.343,-15.672]],"c":true}],"e":[{"i":[[-12.369,-1.388],[2.77,3.145],[0,1.393],[0,0],[0,0]],"o":[[10.544,1.183],[-1.665,-1.89],[0,-7.65],[0,0],[0,0]],"v":[[4.143,8.543],[10.207,3.82],[6.493,-1.492],[0.929,-14.48],[-15.771,-16.335]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.976470589638,0.57647061348,0.57647061348,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.083,16.789],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[103.391,102.914],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"body","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[14]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[14],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[131.075,163.508,0],"e":[125.075,144.508,0],"to":[-1,-3.16666674613953,0],"ti":[0.16666667163372,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[125.075,144.508,0],"e":[130.075,163.508,0],"to":[-0.16666667163372,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[130.075,163.508,0],"e":[125.075,144.508,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[125.075,144.508,0],"e":[130.075,163.508,0],"to":[0,0,0],"ti":[-0.83333331346512,-3.16666674613953,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[57.909,101.248,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[3.119,14.05],[1.443,14.3],[-2.17,13.017],[24.121,3.711],[1.855,-5.566],[0,0],[3.711,-9.276],[-0.497,-4.769],[0.927,-0.928],[1.074,-2.294],[1.392,-5.65],[-12.598,-18.435],[-23.003,15.653]],"o":[[-3.406,-15.344],[-0.176,-1.741],[1.856,-11.133],[-24.12,-3.711],[0,0],[0,0],[-0.436,1.09],[0.861,8.25],[-0.637,0.638],[-2.503,5.341],[-5.042,20.471],[16.307,23.863],[20.023,-13.625]],"v":[[114.214,133.19],[100.314,82.432],[100.948,46.636],[74.972,3.961],[34.153,24.371],[32.297,29.937],[21.165,41.069],[19.24,50.728],[25.804,69.828],[11.164,95.141],[5.292,111.681],[15.526,172.377],[93.31,188.365]],"c":true}],"e":[{"i":[[2.064,8.136],[5.519,9.808],[2.437,12.97],[31.861,-4.221],[0.18,-13.631],[0,0],[1.918,-5.829],[-4.252,-6.457],[0.927,-0.928],[6.669,-10.901],[0.041,-8.441],[-15.193,-18.637],[-23.788,14.432]],"o":[[-3.865,-15.235],[-0.858,-1.525],[-4.115,-21.896],[-12.162,1.611],[0,0],[0,0],[-0.367,1.115],[6.593,10.012],[-0.637,0.638],[-4.793,7.835],[-0.091,18.721],[14.406,17.672],[31.523,-19.125]],"v":[[116.714,116.69],[100.314,82.932],[88.948,43.136],[42.472,-1.539],[16.153,25.371],[16.797,38.187],[11.665,44.569],[11.74,60.728],[23.804,72.328],[12.164,86.641],[4.292,113.681],[19.026,162.377],[92.31,178.365]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[2.064,8.136],[5.519,9.808],[2.437,12.97],[31.861,-4.221],[0.18,-13.631],[0,0],[1.918,-5.829],[-4.252,-6.457],[0.927,-0.928],[6.669,-10.901],[0.041,-8.441],[-15.193,-18.637],[-23.788,14.432]],"o":[[-3.865,-15.235],[-0.858,-1.525],[-4.115,-21.896],[-12.162,1.611],[0,0],[0,0],[-0.367,1.115],[6.593,10.012],[-0.637,0.638],[-4.793,7.835],[-0.091,18.721],[14.406,17.672],[31.523,-19.125]],"v":[[116.714,116.69],[100.314,82.932],[88.948,43.136],[42.472,-1.539],[16.153,25.371],[16.797,38.187],[11.665,44.569],[11.74,60.728],[23.804,72.328],[12.164,86.641],[4.292,113.681],[19.026,162.377],[92.31,178.365]],"c":true}],"e":[{"i":[[3.119,14.05],[1.443,14.3],[-2.17,13.017],[24.121,3.711],[1.855,-5.566],[0,0],[3.711,-9.276],[-0.497,-4.769],[0.927,-0.928],[1.074,-2.294],[1.392,-5.65],[-12.598,-18.435],[-23.003,15.653]],"o":[[-3.406,-15.344],[-0.176,-1.741],[1.856,-11.133],[-24.12,-3.711],[0,0],[0,0],[-0.436,1.09],[0.861,8.25],[-0.637,0.638],[-2.503,5.341],[-5.042,20.471],[16.307,23.863],[20.023,-13.625]],"v":[[114.214,133.19],[100.314,82.432],[100.948,46.636],[74.972,3.961],[34.153,24.371],[32.297,29.937],[21.165,41.069],[19.24,50.728],[25.804,69.828],[11.164,95.141],[5.292,111.681],[15.526,172.377],[93.31,188.365]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[3.119,14.05],[1.443,14.3],[-2.17,13.017],[24.121,3.711],[1.855,-5.566],[0,0],[3.711,-9.276],[-0.497,-4.769],[0.927,-0.928],[1.074,-2.294],[1.392,-5.65],[-12.598,-18.435],[-23.003,15.653]],"o":[[-3.406,-15.344],[-0.176,-1.741],[1.856,-11.133],[-24.12,-3.711],[0,0],[0,0],[-0.436,1.09],[0.861,8.25],[-0.637,0.638],[-2.503,5.341],[-5.042,20.471],[16.307,23.863],[20.023,-13.625]],"v":[[114.214,133.19],[100.314,82.432],[100.948,46.636],[74.972,3.961],[34.153,24.371],[32.297,29.937],[21.165,41.069],[19.24,50.728],[25.804,69.828],[11.164,95.141],[5.292,111.681],[15.526,172.377],[93.31,188.365]],"c":true}],"e":[{"i":[[2.064,8.136],[5.519,9.808],[2.437,12.97],[31.861,-4.221],[0.18,-13.631],[0,0],[1.918,-5.829],[-4.252,-6.457],[0.927,-0.928],[6.669,-10.901],[0.041,-8.441],[-15.193,-18.637],[-23.788,14.432]],"o":[[-3.865,-15.235],[-0.858,-1.525],[-4.115,-21.896],[-12.162,1.611],[0,0],[0,0],[-0.367,1.115],[6.593,10.012],[-0.637,0.638],[-4.793,7.835],[-0.091,18.721],[14.406,17.672],[31.523,-19.125]],"v":[[116.714,116.69],[100.314,82.932],[88.948,43.136],[42.472,-1.539],[16.153,25.371],[16.797,38.187],[11.665,44.569],[11.74,60.728],[23.804,72.328],[12.164,86.641],[4.292,113.681],[19.026,162.377],[92.31,178.365]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[2.064,8.136],[5.519,9.808],[2.437,12.97],[31.861,-4.221],[0.18,-13.631],[0,0],[1.918,-5.829],[-4.252,-6.457],[0.927,-0.928],[6.669,-10.901],[0.041,-8.441],[-15.193,-18.637],[-23.788,14.432]],"o":[[-3.865,-15.235],[-0.858,-1.525],[-4.115,-21.896],[-12.162,1.611],[0,0],[0,0],[-0.367,1.115],[6.593,10.012],[-0.637,0.638],[-4.793,7.835],[-0.091,18.721],[14.406,17.672],[31.523,-19.125]],"v":[[116.714,116.69],[100.314,82.932],[88.948,43.136],[42.472,-1.539],[16.153,25.371],[16.797,38.187],[11.665,44.569],[11.74,60.728],[23.804,72.328],[12.164,86.641],[4.292,113.681],[19.026,162.377],[92.31,178.365]],"c":true}],"e":[{"i":[[3.119,14.05],[1.443,14.3],[-2.17,13.017],[24.121,3.711],[1.855,-5.566],[0,0],[3.711,-9.276],[-0.497,-4.769],[0.927,-0.928],[1.074,-2.294],[1.392,-5.65],[-12.598,-18.435],[-23.003,15.653]],"o":[[-3.406,-15.344],[-0.176,-1.741],[1.856,-11.133],[-24.12,-3.711],[0,0],[0,0],[-0.436,1.09],[0.861,8.25],[-0.637,0.638],[-2.503,5.341],[-5.042,20.471],[16.307,23.863],[20.023,-13.625]],"v":[[114.214,133.19],[100.314,82.432],[100.948,46.636],[74.972,3.961],[34.153,24.371],[32.297,29.937],[21.165,41.069],[19.24,50.728],[25.804,69.828],[11.164,95.141],[5.292,111.681],[15.526,172.377],[93.31,188.365]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.976000019148,0.57599995931,0.57599995931,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"left ear","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.905,119.544,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[76.687,99.836,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[13,-4.5],[0,0],[0,0]],"o":[[-10.459,3.62],[0,0],[0,0]],"v":[[-8,-120.5],[-19.5,-109.5],[1.932,-112.016]],"c":true}],"e":[{"i":[[10.44,-3.65],[0,0],[0,0]],"o":[[-10.448,3.653],[0,0],[0,0]],"v":[[-26.889,-117.04],[-18.018,-107.449],[-7.084,-114.234]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[10.44,-3.65],[0,0],[0,0]],"o":[[-10.448,3.653],[0,0],[0,0]],"v":[[-26.889,-117.04],[-18.018,-107.449],[-7.084,-114.234]],"c":true}],"e":[{"i":[[14.183,-1.941],[0,0],[0,0]],"o":[[-18.722,2.562],[0,0],[0,0]],"v":[[-3.16,-120.237],[-19.5,-109.5],[1.932,-112.016]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[14.183,-1.941],[0,0],[0,0]],"o":[[-18.722,2.562],[0,0],[0,0]],"v":[[-3.16,-120.237],[-19.5,-109.5],[1.932,-112.016]],"c":true}],"e":[{"i":[[10.932,1.606],[0,0],[0,0]],"o":[[-21.367,-3.138],[0,0],[0,0]],"v":[[3.658,-121.26],[-24.796,-110.572],[-6.371,-112.546]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[10.932,1.606],[0,0],[0,0]],"o":[[-21.367,-3.138],[0,0],[0,0]],"v":[[3.658,-121.26],[-24.796,-110.572],[-6.371,-112.546]],"c":true}],"e":[{"i":[[13,-4.5],[0,0],[0,0]],"o":[[-10.459,3.62],[0,0],[0,0]],"v":[[-8,-120.5],[-19.5,-109.5],[1.932,-112.016]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.952941179276,0.509803950787,0.501960813999,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[101.015,100.81],"ix":3},"r":{"a":0,"k":0.022,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"right arm Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[171.186,127.399,0],"ix":2},"a":{"a":0,"k":[52.548,63.288,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-11.104,-11.125],[-10.566,6.314],[-2.469,1.867],[-0.826,0.524],[0.43,1.436],[0.92,0.748],[2.738,-0.091],[2.379,-0.189],[7.807,0.614],[4.254,0.971]],"o":[[10.295,-6.33],[3.554,-2.124],[0.935,-0.707],[4.367,-2.771],[-0.501,-1.673],[-1.395,-1.134],[-1.711,0.057],[-4.64,0.37],[-18.768,-1.478],[8.543,11.558]],"v":[[-19.199,20.792],[15.781,0.985],[24.956,-5.034],[33.911,-2.903],[32.779,-11.406],[28.355,-15.942],[22.268,-17.235],[16.161,-16.8],[-2.301,-16.674],[-32.458,-20.792]],"c":true}],"e":[{"i":[[-11.104,-11.125],[-15.5,5.99],[-2.532,1.977],[-0.818,0.548],[1.315,0.658],[2.733,-1.47],[2.307,-0.855],[2.425,-0.21],[7.873,0.585],[4.207,1.23]],"o":[[10.722,-6.116],[4.08,-1.577],[0.941,-0.73],[4.189,-2.716],[-6.288,-3.145],[-1.576,0.848],[-1.622,0.601],[-4.686,0.403],[-18.771,-1.407],[7.528,11.882]],"v":[[-20.519,21.393],[8.649,4.112],[26.044,-4.509],[33.858,-10.09],[38.489,-23.076],[26.709,-19.924],[22.363,-17.256],[13.134,-18.87],[-8.198,-20.59],[-32.653,-20.553]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":1,"s":[{"i":[[-11.104,-11.125],[-15.5,5.99],[-2.532,1.977],[-0.818,0.548],[1.315,0.658],[2.733,-1.47],[2.307,-0.855],[2.425,-0.21],[7.873,0.585],[4.207,1.23]],"o":[[10.722,-6.116],[4.08,-1.577],[0.941,-0.73],[4.189,-2.716],[-6.288,-3.145],[-1.576,0.848],[-1.622,0.601],[-4.686,0.403],[-18.771,-1.407],[7.528,11.882]],"v":[[-20.519,21.393],[8.649,4.112],[26.044,-4.509],[33.858,-10.09],[38.489,-23.076],[26.709,-19.924],[22.363,-17.256],[13.134,-18.87],[-8.198,-20.59],[-32.653,-20.553]],"c":true}],"e":[{"i":[[-11.104,-11.125],[-14.505,10.067],[-3.542,3.728],[-1.091,0.379],[0.288,0.961],[0.755,0.802],[2.104,0.089],[3.158,-0.545],[8.937,0.129],[3.463,5.376]],"o":[[17.56,-2.697],[6.68,-4.636],[1.042,-1.097],[2.469,-0.858],[-0.436,-1.457],[-1.146,-1.217],[-2.026,-0.086],[-5.416,0.935],[-18.824,-0.272],[-8.717,17.058]],"v":[[-41.654,31.01],[10.463,7.876],[26.189,-5.06],[28.786,-8.112],[33.299,-14.684],[31.518,-23.387],[24.713,-25.943],[16.161,-16.8],[-8.385,-13.15],[-35.767,-16.742]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[-11.104,-11.125],[-14.505,10.067],[-3.542,3.728],[-1.091,0.379],[0.288,0.961],[0.755,0.802],[2.104,0.089],[3.158,-0.545],[8.937,0.129],[3.463,5.376]],"o":[[17.56,-2.697],[6.68,-4.636],[1.042,-1.097],[2.469,-0.858],[-0.436,-1.457],[-1.146,-1.217],[-2.026,-0.086],[-5.416,0.935],[-18.824,-0.272],[-8.717,17.058]],"v":[[-41.654,31.01],[10.463,7.876],[26.189,-5.06],[28.786,-8.112],[33.299,-14.684],[31.518,-23.387],[24.713,-25.943],[16.161,-16.8],[-8.385,-13.15],[-35.767,-16.742]],"c":true}],"e":[{"i":[[-11.104,-11.125],[-9.387,5.292],[-3.92,2.822],[-0.773,0.752],[-0.73,0.992],[5.019,-0.422],[2.101,0.07],[3.195,-0.254],[7.601,1.883],[4.254,0.971]],"o":[[9.446,-0.926],[6.596,-3.719],[1.154,-0.831],[1.526,-1.486],[1.973,-2.682],[-1.59,0.134],[-2.024,-0.067],[-4.64,0.37],[-17.269,-4.278],[8.543,11.558]],"v":[[-19.199,20.792],[9.216,3.798],[25.656,-6.016],[28.563,-8.391],[35.362,-14.602],[32.833,-21.042],[23.922,-17.241],[16.161,-16.8],[-4.885,-17.657],[-32.458,-20.792]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[-11.104,-11.125],[-9.387,5.292],[-3.92,2.822],[-0.773,0.752],[-0.73,0.992],[5.019,-0.422],[2.101,0.07],[3.195,-0.254],[7.601,1.883],[4.254,0.971]],"o":[[9.446,-0.926],[6.596,-3.719],[1.154,-0.831],[1.526,-1.486],[1.973,-2.682],[-1.59,0.134],[-2.024,-0.067],[-4.64,0.37],[-17.269,-4.278],[8.543,11.558]],"v":[[-19.199,20.792],[9.216,3.798],[25.656,-6.016],[28.563,-8.391],[35.362,-14.602],[32.833,-21.042],[23.922,-17.241],[16.161,-16.8],[-4.885,-17.657],[-32.458,-20.792]],"c":true}],"e":[{"i":[[-11.104,-11.125],[-10.566,6.314],[-2.638,2.383],[-0.503,0.637],[-0.393,0.923],[2.901,0.041],[2.651,-0.472],[3.195,-0.254],[7.807,0.614],[4.254,0.971]],"o":[[10.295,-6.33],[4.866,-2.908],[0.776,-0.701],[0.993,-1.259],[1.852,-4.349],[-1.596,-0.023],[-1.993,0.355],[-4.64,0.37],[-18.768,-1.478],[8.543,11.558]],"v":[[-19.199,20.792],[15.781,0.985],[27.397,-7.033],[29.325,-9.043],[32.427,-12.909],[31.801,-24.024],[23.922,-17.241],[16.161,-16.8],[-2.301,-16.674],[-32.458,-20.792]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[-11.104,-11.125],[-10.566,6.314],[-2.638,2.383],[-0.503,0.637],[-0.393,0.923],[2.901,0.041],[2.651,-0.472],[3.195,-0.254],[7.807,0.614],[4.254,0.971]],"o":[[10.295,-6.33],[4.866,-2.908],[0.776,-0.701],[0.993,-1.259],[1.852,-4.349],[-1.596,-0.023],[-1.993,0.355],[-4.64,0.37],[-18.768,-1.478],[8.543,11.558]],"v":[[-19.199,20.792],[15.781,0.985],[27.397,-7.033],[29.325,-9.043],[32.427,-12.909],[31.801,-24.024],[23.922,-17.241],[16.161,-16.8],[-2.301,-16.674],[-32.458,-20.792]],"c":true}],"e":[{"i":[[-11.104,-11.125],[-10.566,6.314],[-2.638,2.383],[-0.503,0.637],[0.288,0.961],[0.747,0.74],[2.101,0.07],[3.195,-0.254],[7.807,0.614],[4.254,0.971]],"o":[[10.295,-6.33],[4.866,-2.908],[0.776,-0.701],[0.993,-1.259],[-0.436,-1.457],[-1.134,-1.123],[-2.024,-0.067],[-4.64,0.37],[-18.768,-1.478],[8.543,11.558]],"v":[[-19.199,20.792],[15.781,0.985],[27.397,-7.033],[29.325,-9.043],[30.454,-12.39],[28.7,-15.633],[23.922,-17.241],[16.161,-16.8],[-2.301,-16.674],[-32.458,-20.792]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[-7.516,-68.234],"to":[-1.25260412693024,-11.3723955154419],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[-7.516,-68.234],"e":[0,0],"to":[0,0],"ti":[0.44322040677071,-8.47819042205811]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[0,0],"e":[-10.175,-17.365],"to":[-0.44322040677071,8.47819042205811],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[-10.175,-17.365],"e":[0,0],"to":[0,0],"ti":[-1.69582462310791,-2.89420580863953]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[101.323,101.673],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-34]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-34],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[25.257]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[25.257],"e":[0]},{"t":47.0000019143492}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.976470589638,0.57647061348,0.57647061348,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[74.24,107.805],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[98.342,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Tail Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-33]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-33],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[-17]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[-17],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[212.335,167.723,0],"e":[190.335,118.723,0],"to":[-3.66666674613953,-8.16666698455811,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[190.335,118.723,0],"e":[212.335,167.723,0],"to":[0,0,0],"ti":[-0.83333331346512,-1.83333337306976,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[212.335,167.723,0],"e":[195.335,129.723,0],"to":[0.83333331346512,1.83333337306976,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[195.335,129.723,0],"e":[212.335,167.723,0],"to":[0,0,0],"ti":[-2.83333325386047,-6.33333349227905,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[54.728,57.385,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[15.626,-43.075],[8.28,-11.357],[-20.938,9.59],[-4.438,25.785],[17.722,4.423],[2.676,0]],"o":[[-6.694,18.454],[12.237,-1.587],[23.8,-10.901],[3.197,-18.576],[-2.934,-0.733],[-23.09,0.001]],"v":[[-30.358,12.605],[-54.478,57.135],[-2.526,42.293],[51.281,-13.37],[30.872,-56.045],[22.465,-57.136]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.952941179276,0.509803950787,0.501960813999,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54.728,57.386],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"left hand","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-62]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[-62],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0],"e":[2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[2],"e":[0]},{"t":47.0000019143492}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[73.59,148.469,0],"e":[66.59,159.469,0],"to":[-1.16666662693024,1.83333337306976,0],"ti":[0.5,0.83333331346512,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[66.59,159.469,0],"e":[70.59,143.469,0],"to":[-0.5,-0.83333331346512,0],"ti":[-2.5,7.16666650772095,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[70.59,143.469,0],"e":[81.59,116.469,0],"to":[2.5,-7.16666650772095,0],"ti":[-0.5,-0.83333331346512,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[81.59,116.469,0],"e":[73.59,148.469,0],"to":[0.5,0.83333331346512,0],"ti":[1.33333337306976,-5.33333349227905,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[26.314,29.276,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[2.354,-5.536],[-1.08,-3.642],[-22.462,-9.92],[-0.008,2.143],[8.241,2.434],[0.346,0.758],[2.55,0.62]],"o":[[-1.514,3.559],[2.627,8.841],[-1.75,-5.549],[-4.541,0.998],[-7.681,-2.268],[-0.844,-1.846],[-4.134,0.346]],"v":[[-24.551,-20.684],[-24.479,-9.865],[9.958,29.026],[26.064,-6.89],[3.748,-10.381],[-10.835,-16.004],[-14.424,-29.026]],"c":true}],"e":[{"i":[[-2.873,-10.082],[-1.08,-3.642],[-27.836,-10.69],[-0.008,2.143],[7.525,2.066],[0.88,0.302],[2.55,0.62]],"o":[[1.06,3.72],[2.627,8.841],[-1.75,-5.549],[-4.045,0.889],[-8.797,-2.415],[-5.212,-1.789],[1.105,-8.394]],"v":[[-30.277,-15.063],[-24.157,-9.482],[9.958,29.026],[38.112,-0.636],[3.547,-8.438],[-10.835,-16.004],[-22.437,-22.629]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":17,"s":[{"i":[[-2.873,-10.082],[-1.08,-3.642],[-27.836,-10.69],[-0.008,2.143],[7.525,2.066],[0.88,0.302],[2.55,0.62]],"o":[[1.06,3.72],[2.627,8.841],[-1.75,-5.549],[-4.045,0.889],[-8.797,-2.415],[-5.212,-1.789],[1.105,-8.394]],"v":[[-30.277,-15.063],[-24.157,-9.482],[9.958,29.026],[38.112,-0.636],[3.547,-8.438],[-10.835,-16.004],[-22.437,-22.629]],"c":true}],"e":[{"i":[[2.354,-5.536],[-1.08,-3.642],[-10.4,-18.741],[-0.008,2.143],[7.525,2.066],[0.386,0.847],[2.55,0.62]],"o":[[-1.514,3.559],[2.627,8.841],[-1.75,-5.549],[-4.045,0.889],[-8.797,-2.415],[-0.844,-1.846],[-4.134,0.346]],"v":[[-24.551,-20.684],[-24.479,-9.865],[9.958,29.026],[33.529,-4.995],[8.487,-12.851],[-10.835,-16.004],[-14.424,-29.026]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":22,"s":[{"i":[[2.354,-5.536],[-1.08,-3.642],[-10.4,-18.741],[-0.008,2.143],[7.525,2.066],[0.386,0.847],[2.55,0.62]],"o":[[-1.514,3.559],[2.627,8.841],[-1.75,-5.549],[-4.045,0.889],[-8.797,-2.415],[-0.844,-1.846],[-4.134,0.346]],"v":[[-24.551,-20.684],[-24.479,-9.865],[9.958,29.026],[33.529,-4.995],[8.487,-12.851],[-10.835,-16.004],[-14.424,-29.026]],"c":true}],"e":[{"i":[[-2.873,-10.082],[-1.08,-3.642],[-27.836,-10.69],[-0.008,2.143],[7.525,2.066],[0.88,0.302],[2.55,0.62]],"o":[[1.06,3.72],[2.627,8.841],[-1.75,-5.549],[-4.045,0.889],[-8.797,-2.415],[-5.212,-1.789],[1.105,-8.394]],"v":[[-30.277,-15.063],[-24.157,-9.482],[9.958,29.026],[38.112,-0.636],[3.547,-8.438],[-10.835,-16.004],[-22.437,-22.629]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42,"s":[{"i":[[-2.873,-10.082],[-1.08,-3.642],[-27.836,-10.69],[-0.008,2.143],[7.525,2.066],[0.88,0.302],[2.55,0.62]],"o":[[1.06,3.72],[2.627,8.841],[-1.75,-5.549],[-4.045,0.889],[-8.797,-2.415],[-5.212,-1.789],[1.105,-8.394]],"v":[[-30.277,-15.063],[-24.157,-9.482],[9.958,29.026],[38.112,-0.636],[3.547,-8.438],[-10.835,-16.004],[-22.437,-22.629]],"c":true}],"e":[{"i":[[2.354,-5.536],[-1.08,-3.642],[-22.462,-9.92],[-0.008,2.143],[8.241,2.434],[0.346,0.758],[2.55,0.62]],"o":[[-1.514,3.559],[2.627,8.841],[-1.75,-5.549],[-4.541,0.998],[-7.681,-2.268],[-0.844,-1.846],[-4.134,0.346]],"v":[[-24.551,-20.684],[-24.479,-9.865],[9.958,29.026],[26.064,-6.89],[3.748,-10.381],[-10.835,-16.004],[-14.424,-29.026]],"c":true}]},{"t":47.0000019143492}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0.152,0.512],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"mm","mm":4,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.951700389385,0.511714220047,0.503170788288,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.602,29.879],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"shadow","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[145,183,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[109.014,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[64.705,22.852],"e":[30,10.595]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":17,"s":[30,10.595],"e":[64.705,22.852]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":22,"s":[64.705,22.852],"e":[30,10.595]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":42,"s":[30,10.595],"e":[64.705,22.852]},{"t":46.0000018736184}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.532000601292,0.344732820988,0.344732820988,1],"e":[0.361688107252,0.33187431097,0.33187431097,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[0.361688107252,0.33187431097,0.33187431097,1],"e":[0.532000601292,0.344732820988,0.344732820988,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0.532000601292,0.344732820988,0.344732820988,1],"e":[0.361688107252,0.33187431097,0.33187431097,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[0.361688107252,0.33187431097,0.33187431097,1],"e":[0.532000601292,0.344732820988,0.344732820988,1]},{"t":46.0000018736184}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[40],"e":[20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[20],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[40],"e":[20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[20],"e":[40]},{"t":46.0000018736184}],"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-13.212,107.812],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[92.827,156.063],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":167.000006802049,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/confetti.json b/example/resource/confetti.json
new file mode 100644 (file)
index 0000000..495035c
--- /dev/null
@@ -0,0 +1 @@
+{"v":"4.12.0","fr":29.9700012207031,"ip":0,"op":59.0000024031193,"w":800,"h":800,"nm":"confettis2","ddd":0,"assets":[{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"p20","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":4,"s":[100],"e":[0]},{"t":43.0000017514259}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":4,"s":[400,400,0],"e":[71,152,0],"to":[1.83333337306976,-220.66667175293,0],"ti":[85.1666641235352,-23.3333339691162,0]},{"t":43.0000017514259}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.901960790157,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":4.00000016292334,"op":364.000014826024,"st":4.00000016292334,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"p19","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":3,"s":[100],"e":[0]},{"t":42.0000017106951}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":3,"s":[400,400,0],"e":[579,746,0],"to":[101.833335876465,91.3333358764648,0],"ti":[27.1666660308838,-153.33332824707,0]},{"t":42.0000017106951}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3.00000012219251,"op":363.000014785293,"st":3.00000012219251,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"p18","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":2,"s":[100],"e":[0]},{"t":41.0000016699642}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":2,"s":[400,400,0],"e":[503,238,0],"to":[-84.1666641235352,-74.6666641235352,0],"ti":[-88.8333358764648,-45.3333320617676,0]},{"t":41.0000016699642}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.737254917622,0.172549024224,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2.00000008146167,"op":362.000014744562,"st":2.00000008146167,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"p17","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":1,"s":[100],"e":[0]},{"t":40.0000016292334}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":1,"s":[400,400,0],"e":[91,556,0],"to":[-82.1666641235352,-112.666664123535,0],"ti":[73.1666641235352,-199.33332824707,0]},{"t":40.0000016292334}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1.00000004073083,"op":361.000014703831,"st":1.00000004073083,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"p16","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":0,"s":[100],"e":[0]},{"t":39.0000015885026}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":0,"s":[400,400,0],"e":[511,308,0],"to":[77.8333358764648,91.3333358764648,0],"ti":[51.1666679382324,60.6666679382324,0]},{"t":39.0000015885026}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.270588248968,0.529411792755,0.952941179276,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360.000014663101,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"p15","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":1,"s":[100],"e":[0]},{"t":40.0000016292334}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":1,"s":[400,400,0],"e":[155,280,0],"to":[-30.1666660308838,-122.666664123535,0],"ti":[95.1666641235352,-53.3333320617676,0]},{"t":40.0000016292334}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.901960790157,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1.00000004073083,"op":361.000014703831,"st":1.00000004073083,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"p14","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":2,"s":[100],"e":[0]},{"t":41.0000016699642}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":2,"s":[400,400,0],"e":[681,388.872,0],"to":[143.83332824707,49.3333320617676,0],"ti":[-58.8333320617676,48.6666679382324,0]},{"t":41.0000016699642}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2.00000008146167,"op":362.000014744562,"st":2.00000008146167,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"p13","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":3,"s":[100],"e":[0]},{"t":42.0000017106951}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":3,"s":[400,400,0],"e":[257,284,0],"to":[-24.1666660308838,-72.6666641235352,0],"ti":[75.1666641235352,-5.33333349227905,0]},{"t":42.0000017106951}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.737254917622,0.172549024224,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3.00000012219251,"op":363.000014785293,"st":3.00000012219251,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"p12","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":4,"s":[100],"e":[0]},{"t":43.0000017514259}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":4,"s":[400,400,0],"e":[301,474,0],"to":[-14.1666669845581,31.3333339691162,0],"ti":[65.1666641235352,-13.3333330154419,0]},{"t":43.0000017514259}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":4.00000016292334,"op":364.000014826024,"st":4.00000016292334,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"p11","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":0,"s":[100],"e":[0]},{"t":39.0000015885026}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":0,"s":[400,400,0],"e":[499,570,0],"to":[-8.16666698455811,63.3333320617676,0],"ti":[-62.8333320617676,-29.3333339691162,0]},{"t":39.0000015885026}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.270588248968,0.529411792755,0.952941179276,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360.000014663101,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"p10","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":1,"s":[100],"e":[0]},{"t":40.0000016292334}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":1,"s":[400,400,0],"e":[557,68,0],"to":[81.8333358764648,-104.666664123535,0],"ti":[-58.8333320617676,104.666664123535,0]},{"t":40.0000016292334}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.901960790157,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1.00000004073083,"op":361.000014703831,"st":1.00000004073083,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"p9","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":2,"s":[100],"e":[0]},{"t":41.0000016699642}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":2,"s":[400,400,0],"e":[715,138,0],"to":[151.83332824707,-46.6666679382324,0],"ti":[-26.8333339691162,72.6666641235352,0]},{"t":41.0000016699642}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2.00000008146167,"op":362.000014744562,"st":2.00000008146167,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"p8","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":3,"s":[100],"e":[0]},{"t":42.0000017106951}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":3,"s":[400,400,0],"e":[679,664,0],"to":[107.833335876465,89.3333358764648,0],"ti":[-92.8333358764648,-103.333335876465,0]},{"t":42.0000017106951}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.074509806931,0.737254917622,0.172549024224,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3.00000012219251,"op":363.000014785293,"st":3.00000012219251,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"p7","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":4,"s":[100],"e":[0]},{"t":43.0000017514259}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":4,"s":[400,400,0],"e":[97,686,0],"to":[-36.1666679382324,53.3333320617676,0],"ti":[75.1666641235352,-39.3333320617676,0]},{"t":43.0000017514259}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":4.00000016292334,"op":364.000014826024,"st":4.00000016292334,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"p6","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":5,"s":[100],"e":[0]},{"t":44.0000017921567}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":5,"s":[400,400,0],"e":[65,340,0],"to":[-148.16667175293,-132.66667175293,0],"ti":[61.1666679382324,-65.3333358764648,0]},{"t":44.0000017921567}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.270588248968,0.529411792755,0.952941179276,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5.00000020365417,"op":365.000014866755,"st":5.00000020365417,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"p5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":6,"s":[100],"e":[0]},{"t":45.0000018328876}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.004,"y":0.691},"o":{"x":0.014,"y":0},"n":"0p004_0p691_0p014_0","t":6,"s":[400,400,0],"e":[400,23.613,0],"to":[-55.3803939819336,-168.204071044922,0],"ti":[27.0301876068115,124.269813537598,0]},{"t":45.0000018328876}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.901960790157,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":6.00000024438501,"op":366.000014907486,"st":6.00000024438501,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"p4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":7,"s":[100],"e":[0]},{"t":46.0000018736184}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":7,"s":[400,400,0],"e":[303,660,0],"to":[109.833335876465,69.3333358764648,0],"ti":[97.1666641235352,0.66666668653488,0]},{"t":46.0000018736184}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":7.00000028511585,"op":367.000014948216,"st":7.00000028511585,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"p3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":8,"s":[100],"e":[0]},{"t":47.0000019143492}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":8,"s":[400,400,0],"e":[663,498,0],"to":[41.8333320617676,109.333335876465,0],"ti":[-71.8333358764648,39.6666679382324,0]},{"t":47.0000019143492}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.076272718608,0.735462605953,0.171031266451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":8.00000032584668,"op":368.000014988947,"st":8.00000032584668,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"p2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":9,"s":[100],"e":[0]},{"t":48.0000019550801}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":9,"s":[400,400,0],"e":[187,88,0],"to":[-0.16666667163372,-158.66667175293,0],"ti":[89.1666641235352,6.66666650772095,0]},{"t":48.0000019550801}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.271778345108,0.528400123119,0.952267169952,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":9.00000036657752,"op":369.000015029678,"st":9.00000036657752,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"p1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.01],"y":[0]},"n":["1_1_0p01_0"],"t":0,"s":[100],"e":[0]},{"t":39.0000015885026}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.01,"y":0},"n":"0_1_0p01_0","t":0,"s":[400,400,0],"e":[621,234,0],"to":[-0.16666667163372,-158.66667175293,0],"ti":[-92.8333358764648,-103.333335876465,0]},{"t":39.0000015885026}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.855,0],[0,-7.855],[-7.855,0],[0,7.855]],"o":[[-7.855,0],[0,7.855],[7.855,0],[0,-7.855]],"v":[[0,-14.223],[-14.223,0],[0,14.223],[14.223,0]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.903676450253,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[80,80],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360.000014663101,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":42,"ix":10},"p":{"a":0,"k":[396,400,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[91.5,91.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":9.00000036657752,"op":369.000015029678,"st":9.00000036657752,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":267,"ix":10},"p":{"a":0,"k":[416,420,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[64.5,64.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":7.00000028511585,"op":367.000014948216,"st":7.00000028511585,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":358,"ix":10},"p":{"a":0,"k":[436,400,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[64.5,64.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":5.00000020365417,"op":365.000014866755,"st":5.00000020365417,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":177,"ix":10},"p":{"a":0,"k":[416,380,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[64.5,64.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":3.00000012219251,"op":363.000014785293,"st":3.00000012219251,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":132,"ix":10},"p":{"a":0,"k":[416,400,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[91.5,91.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":1.00000004073083,"op":361.000014703831,"st":1.00000004073083,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[396,400,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[91.5,91.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":8.00000032584668,"op":368.000014988947,"st":8.00000032584668,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":135,"ix":10},"p":{"a":0,"k":[416,420,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[64.5,64.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":6.00000024438501,"op":366.000014907486,"st":6.00000024438501,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":226,"ix":10},"p":{"a":0,"k":[436,400,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[64.5,64.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":4.00000016292334,"op":364.000014826024,"st":4.00000016292334,"bm":0},{"ddd":0,"ind":9,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[416,380,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[64.5,64.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":2.00000008146167,"op":362.000014744562,"st":2.00000008146167,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"confettis1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[416,400,0],"ix":2},"a":{"a":0,"k":[400,400,0],"ix":1},"s":{"a":0,"k":[91.5,91.5,100],"ix":6}},"ao":0,"w":800,"h":800,"ip":0,"op":360.000014663101,"st":0,"bm":0}]}
\ No newline at end of file
diff --git a/example/resource/crunches.json b/example/resource/crunches.json
new file mode 100755 (executable)
index 0000000..73c0121
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":0,"op":145,"w":200,"h":200,"nm":"crunches","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.539,100.996,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.473,-1.118],[-1.989,-1.65],[-1.284,0.567],[0.251,1.628]],"o":[[-1.342,-1.164],[-1.927,1.461],[1.08,0.896],[1.73,-0.764],[0,0]],"v":[[-11.176,-7.404],[-16.007,-7.559],[-16.101,-1.52],[-12.186,-0.974],[-9.898,-5.095]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.438,6.953],"ix":2},"a":{"a":0,"k":[3.438,6.953],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":21,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":69,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":117,"s":[25],"e":[0]},{"t":144}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0.409,0.451],[0.604,-0.063],[0,0],[0.251,1.628],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.215,-1.029],[-3.595,0],[0,0],[-1.66,1.26]],"o":[[0,0],[0,0],[0,0],[0.121,-0.596],[-0.409,-0.449],[0,0],[-1.342,-1.164],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.983,1.252],[2.743,2.324],[0,0],[2.084,0],[0,0]],"v":[[1.707,2.891],[-2.509,2.815],[-5.914,0.3],[-3.283,-6.308],[-3.714,-8.037],[-5.326,-8.653],[-11.176,-7.404],[-9.898,-5.095],[-6.652,-5.636],[-6.762,-5.477],[-6.747,-5.513],[-9.383,-1.929],[-11.629,1.03],[-11.216,5.116],[-1.402,8.716],[-1.002,8.716],[4.769,6.774]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.453,6.953],"ix":2},"a":{"a":0,"k":[3.453,6.953],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":21,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":69,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":117,"s":[25],"e":[0]},{"t":144}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body top","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.73,0.716],[0,0],[1.121,-0.761],[0,0],[0,0],[0,0],[-1.66,1.26],[0,0],[0,0],[-0.883,0.883]],"o":[[0,0],[-0.961,-0.956],[0,0],[0,0],[0,0],[2.084,0],[0,0],[0,0],[0.341,0.247],[0.679,-0.678]],"v":[[17.34,5.004],[11.346,-1.059],[7.762,-1.394],[1.707,2.891],[-2.509,2.815],[0.06,6.434],[4.769,6.774],[9.442,2.983],[14.991,7.558],[17.412,7.557]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body bottom","np":3,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/data.json b/example/resource/data.json
new file mode 100755 (executable)
index 0000000..c3531ce
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.1.13","fr":60,"ip":0,"op":60,"w":300,"h":300,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[43]},{"t":50}],"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[146,146],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.923041880131,0.933333337307,0.058562088758,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[2],"e":[14]},{"t":50}],"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.937254905701,0.033079583198,0.118178516626,1],"e":[0.043737020344,0.085415646434,0.929411768913,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":25,"s":[0.043737020344,0.085415646434,0.929411768913,1],"e":[0.129419133067,0.92549020052,0.068958088756,1]},{"t":55}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":60,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/emoji_wink.json b/example/resource/emoji_wink.json
new file mode 100644 (file)
index 0000000..29b39a5
--- /dev/null
@@ -0,0 +1 @@
+{"v":"4.5.7","fr":30,"ip":0,"op":60,"w":100,"h":100,"ddd":0,"assets":[{"id":"comp_38","layers":[{"ddd":0,"ind":0,"ty":4,"nm":"round_normal","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[50,50,0]},"a":{"a":0,"k":[-252,-412,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-17.673,0],[0,-17.673],[17.673,0],[0,17.673]],"o":[[17.673,0],[0,17.673],[-17.673,0],[0,-17.673]],"v":[[-252,-444],[-220,-412],[-252,-380],[-284,-412]],"c":true}},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[1,0.88,0.59,1]},"o":{"a":0,"k":100},"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":300,"st":0,"bm":0,"sr":1}]}],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"eyes_normal","parent":2,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[50,45.5,0]},"a":{"a":0,"k":[-252,-416.5,0]},"s":{"a":1,"k":[{"i":{"x":[0.516,0.831,0.667],"y":[0.516,1,0.667]},"o":{"x":[0.75,0.705,0.333],"y":[0.75,0,0.333]},"n":["0p516_0p516_0p75_0p75","0p831_1_0p705_0","0p667_0p667_0p333_0p333"],"t":40,"s":[100,100,100],"e":[100,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[0.667,1,0.667]},"o":{"x":[0.333,0.333,0.333],"y":[0.333,0,0.333]},"n":["0p667_0p667_0p333_0p333","0p667_1_0p333_0","0p667_0p667_0p333_0p333"],"t":45,"s":[100,0,100],"e":[100,110,100]},{"i":{"x":[0.298,0.276,0.667],"y":[0.298,1,0.667]},"o":{"x":[0.038,0.105,0.333],"y":[0.038,0,0.333]},"n":["0p298_0p298_0p038_0p038","0p276_1_0p105_0","0p667_0p667_0p333_0p333"],"t":50,"s":[100,110,100],"e":[100,98,100]},{"i":{"x":[0.667,0.667,0.667],"y":[0.667,1,0.667]},"o":{"x":[0.333,0.333,0.333],"y":[0.333,0,0.333]},"n":["0p667_0p667_0p333_0p333","0p667_1_0p333_0","0p667_0p667_0p333_0p333"],"t":55,"s":[100,98,100],"e":[100,100,100]},{"t":60}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.313,"y":1},"o":{"x":0.467,"y":0},"n":"0p313_1_0p467_0","t":6,"s":[{"i":[[-1.933,0],[0,-1.933],[1.933,0],[0,1.933]],"o":[[1.933,0],[0,1.933],[-1.933,0],[0,-1.933]],"v":[[-237.5,-420],[-234,-416.5],[-237.5,-413],[-241,-416.5]],"c":true}],"e":[{"i":[[-1.933,0],[-0.312,-1.313],[1.933,0],[0,1.062]],"o":[[1.933,0],[0.174,0.732],[-1.933,0],[0,-1.25]],"v":[[-237.437,-418],[-231.25,-415.875],[-237.5,-416.938],[-243.188,-415.937]],"c":true}]},{"i":{"x":0.395,"y":1},"o":{"x":0.716,"y":0},"n":"0p395_1_0p716_0","t":16,"s":[{"i":[[-1.933,0],[-0.312,-1.313],[1.933,0],[0,1.062]],"o":[[1.933,0],[0.174,0.732],[-1.933,0],[0,-1.25]],"v":[[-237.437,-418],[-231.25,-415.875],[-237.5,-416.938],[-243.188,-415.937]],"c":true}],"e":[{"i":[[-1.933,0],[0,-1.933],[1.933,0],[0,1.933]],"o":[[1.933,0],[0,1.933],[-1.933,0],[0,-1.933]],"v":[[-237.5,-420],[-234,-416.5],[-237.5,-413],[-241,-416.5]],"c":true}]},{"t":29}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.933,0],[0,-1.933],[1.933,0],[0,1.933]],"o":[[1.933,0],[0,1.933],[-1.933,0],[0,-1.933]],"v":[[-266.5,-420],[-263,-416.5],[-266.5,-413],[-270,-416.5]],"c":true}},"nm":"Path 2","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.33,0.33,0.28,1]},"o":{"a":0,"k":100},"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"mn":"ADBE Vector Group"}],"ip":0,"op":61,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"mouth_smile","parent":2,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.25],"y":[1]},"o":{"x":[0.75],"y":[0]},"n":["0p25_1_0p75_0"],"t":4,"s":[0],"e":[-15]},{"i":{"x":[0.25],"y":[1]},"o":{"x":[0.75],"y":[0]},"n":["0p25_1_0p75_0"],"t":15,"s":[-15],"e":[0]},{"t":30}]},"p":{"a":0,"k":[50.862,57.489,0]},"a":{"a":0,"k":[-251.138,-404.511,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.292,"y":1},"o":{"x":0.506,"y":0},"n":"0p292_1_0p506_0","t":0,"s":[{"i":[[6.254,0],[2.91,1.715],[-0.616,0.558],[-0.707,-0.436],[-2.276,0],[-3.615,1.995],[-0.52,-0.71],[0.737,-0.384]],"o":[[-6.249,0],[-0.716,-0.422],[0.615,-0.558],[3.273,2.017],[2.662,0],[0.728,-0.402],[0.324,0.602],[-3.252,1.696]],"v":[[-252.006,-391.01],[-263.629,-394.434],[-263.522,-396.373],[-261.398,-396.454],[-252.006,-394.02],[-242.635,-396.433],[-240.261,-396.227],[-240.405,-394.414]],"c":true}],"e":[{"i":[[6.233,0],[4.195,4.639],[-0.616,0.558],[-0.558,-0.615],[-5.401,0],[-3.206,6.717],[-0.615,-0.557],[0.338,-0.759]],"o":[[-6.249,0],[-0.557,-0.616],[0.615,-0.558],[3.626,4.01],[4.636,0],[0.358,-0.75],[0.616,0.557],[-2.811,6.323]],"v":[[-252.044,-390.197],[-266.129,-396.746],[-266.022,-398.873],[-263.898,-398.767],[-252.076,-392.959],[-239.687,-401.031],[-237.564,-401.138],[-237.457,-399.012]],"c":true}]},{"i":{"x":0.564,"y":1},"o":{"x":0.571,"y":0},"n":"0p564_1_0p571_0","t":15,"s":[{"i":[[6.233,0],[4.195,4.639],[-0.616,0.558],[-0.558,-0.615],[-5.401,0],[-3.206,6.717],[-0.615,-0.557],[0.338,-0.759]],"o":[[-6.249,0],[-0.557,-0.616],[0.615,-0.558],[3.626,4.01],[4.636,0],[0.358,-0.75],[0.616,0.557],[-2.811,6.323]],"v":[[-252.044,-390.197],[-266.129,-396.746],[-266.022,-398.873],[-263.898,-398.767],[-252.076,-392.959],[-239.687,-401.031],[-237.564,-401.138],[-237.457,-399.012]],"c":true}],"e":[{"i":[[6.254,0],[2.91,1.715],[-0.616,0.558],[-0.707,-0.436],[-2.276,0],[-3.615,1.995],[-0.52,-0.71],[0.737,-0.384]],"o":[[-6.249,0],[-0.716,-0.422],[0.615,-0.558],[3.273,2.017],[2.662,0],[0.728,-0.402],[0.324,0.602],[-3.252,1.696]],"v":[[-252.006,-391.01],[-263.629,-394.434],[-263.522,-396.373],[-261.398,-396.454],[-252.006,-394.02],[-242.635,-396.433],[-240.261,-396.227],[-240.405,-394.414]],"c":true}]},{"i":{"x":0.681,"y":0.833},"o":{"x":0.546,"y":0},"n":"0p681_0p833_0p546_0","t":30,"s":[{"i":[[6.254,0],[2.91,1.715],[-0.616,0.558],[-0.707,-0.436],[-2.276,0],[-3.615,1.995],[-0.52,-0.71],[0.737,-0.384]],"o":[[-6.249,0],[-0.716,-0.422],[0.615,-0.558],[3.273,2.017],[2.662,0],[0.728,-0.402],[0.324,0.602],[-3.252,1.696]],"v":[[-252.006,-391.01],[-263.629,-394.434],[-263.522,-396.373],[-261.398,-396.454],[-252.006,-394.02],[-242.635,-396.433],[-240.261,-396.227],[-240.405,-394.414]],"c":true}],"e":[{"i":[[6.254,0],[2.91,1.715],[-0.616,0.558],[-0.734,-0.389],[-2.276,0],[-3.487,2.248],[-0.52,-0.71],[0.673,-0.488]],"o":[[-6.249,0],[-0.716,-0.422],[0.615,-0.558],[3.089,1.638],[2.662,0],[0.699,-0.451],[0.324,0.602],[-2.905,2.105]],"v":[[-252.006,-391.01],[-264.507,-394.555],[-264.401,-396.494],[-262.276,-396.576],[-252.006,-394.02],[-242.076,-396.998],[-239.702,-396.793],[-239.845,-394.98]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.497,"y":0.261},"n":"0p667_1_0p497_0p261","t":40,"s":[{"i":[[6.254,0],[2.91,1.715],[-0.616,0.558],[-0.734,-0.389],[-2.276,0],[-3.487,2.248],[-0.52,-0.71],[0.673,-0.488]],"o":[[-6.249,0],[-0.716,-0.422],[0.615,-0.558],[3.089,1.638],[2.662,0],[0.699,-0.451],[0.324,0.602],[-2.905,2.105]],"v":[[-252.006,-391.01],[-264.507,-394.555],[-264.401,-396.494],[-262.276,-396.576],[-252.006,-394.02],[-242.076,-396.998],[-239.702,-396.793],[-239.845,-394.98]],"c":true}],"e":[{"i":[[6.254,0],[2.91,1.715],[-0.616,0.558],[-0.707,-0.436],[-2.276,0],[-3.615,1.995],[-0.52,-0.71],[0.737,-0.384]],"o":[[-6.249,0],[-0.716,-0.422],[0.615,-0.558],[3.273,2.017],[2.662,0],[0.728,-0.402],[0.324,0.602],[-3.252,1.696]],"v":[[-252.006,-391.01],[-263.629,-394.434],[-263.522,-396.373],[-261.398,-396.454],[-252.006,-394.02],[-242.635,-396.433],[-240.261,-396.227],[-240.405,-394.414]],"c":true}]},{"t":60}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.33,0.33,0.28,1]},"o":{"a":0,"k":100},"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":61,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":0,"nm":"base_normal","refId":"comp_38","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.273],"y":[1]},"o":{"x":[0.464],"y":[0]},"n":["0p273_1_0p464_0"],"t":4,"s":[0],"e":[13]},{"i":{"x":[0.532],"y":[1]},"o":{"x":[0.578],"y":[0]},"n":["0p532_1_0p578_0"],"t":19,"s":[13],"e":[0]},{"t":40}]},"p":{"a":0,"k":[50,50,0]},"a":{"a":0,"k":[50,50,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":100,"h":100,"ip":0,"op":61,"st":0,"bm":0,"sr":1}]}
\ No newline at end of file
diff --git a/example/resource/glow_loading.json b/example/resource/glow_loading.json
new file mode 100644 (file)
index 0000000..3cbcc05
--- /dev/null
@@ -0,0 +1 @@
+{"v":"4.6.8","fr":29.9700012207031,"ip":0,"op":69.0000028104276,"w":256,"h":256,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Glow ball","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[360]},{"t":69.0000028104276}]},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":0,"k":[132,132,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.1635217,0.8509804,0.8105415,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 8","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":315},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":56,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":59,"s":[145,145,100],"e":[132,132,100]},{"t":63.0000025660426}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":56,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":62,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":69.0000028104276}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 7","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":270},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":48,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":51,"s":[145,145,100],"e":[132,132,100]},{"t":55.0000022401959}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.6745098,0.8431373,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":47,"s":[0.1647059,0.6745098,0.8431373,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":58,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":69.0000028104276}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 6","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":225},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":39,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":42,"s":[145,145,100],"e":[132,132,100]},{"t":46.0000018736184}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":37,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":43,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":48.0000019550801}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":180},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":31,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":34,"s":[145,145,100],"e":[132,132,100]},{"t":38.0000015477717}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":26,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":38,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":42.0000017106951}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":135},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":23,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":26,"s":[145,145,100],"e":[132,132,100]},{"t":30.0000012219251}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":38.0000015477717}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":90},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":14,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":17,"s":[145,145,100],"e":[132,132,100]},{"t":21.0000008553475}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":22,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":28.0000011404634}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":45},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":7,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":10,"s":[145,145,100],"e":[132,132,100]},{"t":14.0000005702317}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":22.0000008960784}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[127.984,127.969,0]},"a":{"a":0,"k":[-0.182,32.385,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[132,132,100],"e":[145,145,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":3,"s":[145,145,100],"e":[132,132,100]},{"t":7.00000028511585}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.125,14.125]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0.1647059,0.6313726,0.8509804,1],"e":[0.1647059,0.8509804,0.8117647,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":5,"s":[0.1647059,0.8509804,0.8117647,1],"e":[0.1647059,0.6313726,0.8509804,1]},{"t":16.0000006516934}]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-0.063,1.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1}]}
\ No newline at end of file
diff --git a/example/resource/icon_animation.json b/example/resource/icon_animation.json
new file mode 100755 (executable)
index 0000000..c574f74
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.1.13","fr":33.8690032958984,"ip":0,"op":90.000932593746,"w":276,"h":580,"nm":"IconAnimation","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,102,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.438,-16.813],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.66,4.158],[0.55,-0.255],[0.162,-0.111],[0,0],[0.052,-0.433],[0,0],[-0.72,-0.085],[-0.053,0],[-0.079,0.669],[0,0],[0,0]],"o":[[-0.122,0.001],[-0.175,0.062],[0,0],[-0.358,0.248],[0,0],[-0.083,0.72],[0.053,0.006],[0.656,0],[0,0],[0,0],[0,0]],"v":[[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-6.499,-11.32],[-7.142,-10.25],[-7.54,-4.643],[-6.387,-3.183],[-6.23,-3.174],[-4.926,-4.334],[-4.207,-9.214],[-2.379,-10.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.219,-10.875],"ix":2},"a":{"a":0,"k":[1.219,-15.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60.964,"s":[-40],"e":[-71]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":76.205,"s":[-71],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":86.366,"s":[-40],"e":[0]},{"t":101.607009887695}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.091,-0.063],[0,0],[-0.282,0],[-0.276,0.395],[0.644,0.449],[0,0],[0,0],[0.035,0.064]],"o":[[0.074,0.082],[0,0],[0.247,0.175],[0.448,0],[0.447,-0.644],[0,0],[0,0],[-0.291,-1.581],[-4.465,2.376]],"v":[[4.208,-6.719],[4.455,-6.499],[8.738,-3.549],[9.55,-3.292],[10.717,-3.899],[10.366,-5.878],[6.496,-8.672],[4.938,-13.658],[1.653,-16.626]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.313,-10.375],"ix":2},"a":{"a":0,"k":[1.313,-15.375],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60.964,"s":[40],"e":[78]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":76.205,"s":[78],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":86.366,"s":[40],"e":[0]},{"t":101.607009887695}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":50.804,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60.964,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":71.125,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":76.205,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":86.366,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":96.527,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"t":101.607009887695}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.063,2.75],"ix":2},"a":{"a":0,"k":[-0.063,-2.25],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[-42]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60.964,"s":[-42],"e":[-57]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":71.125,"s":[-57],"e":[-59]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":76.205,"s":[-59],"e":[-25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":86.366,"s":[-25],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96.527,"s":[4],"e":[0]},{"t":101.607009887695}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":50.804,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60.964,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":71.125,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":76.205,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":86.366,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":96.527,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"t":101.607009887695}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.094,3.281],"ix":2},"a":{"a":0,"k":[1.094,-1.719],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[34]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60.964,"s":[34],"e":[64]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":71.125,"s":[64],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":76.205,"s":[60],"e":[18]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":86.366,"s":[18],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96.527,"s":[4],"e":[0]},{"t":101.607009887695}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.013,0.152],[0,0],[-0.091,-0.063],[1.678,0.136],[0.55,-0.255],[0.162,-0.111],[0,0],[0,0],[-0.022,-0.203],[-1.843,0.062]],"o":[[0.034,-0.145],[0,0],[0.074,0.082],[0,0],[-0.65,-0.052],[-0.175,0.062],[0,0],[0,0],[-0.018,0.211],[0,0],[1.843,-0.062]],"v":[[3.59,-2.105],[3.664,-2.552],[4.208,-6.719],[4.455,-6.499],[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-2.379,-10.24],[-2.626,-3.062],[-2.616,-2.439],[0.782,2.375]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":50.8035049438477,"op":389.493537902832,"st":50.8035049438477,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,102,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.438,-16.813],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.66,4.158],[0.55,-0.255],[0.162,-0.111],[0,0],[0.052,-0.433],[0,0],[-0.72,-0.085],[-0.053,0],[-0.079,0.669],[0,0],[0,0]],"o":[[-0.122,0.001],[-0.175,0.062],[0,0],[-0.358,0.248],[0,0],[-0.083,0.72],[0.053,0.006],[0.656,0],[0,0],[0,0],[0,0]],"v":[[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-6.499,-11.32],[-7.142,-10.25],[-7.54,-4.643],[-6.387,-3.183],[-6.23,-3.174],[-4.926,-4.334],[-4.207,-9.214],[-2.379,-10.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.219,-10.875],"ix":2},"a":{"a":0,"k":[1.219,-15.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":10.161,"s":[-40],"e":[-71]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":25.402,"s":[-71],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":35.562,"s":[-40],"e":[0]},{"t":50.8035049438477}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.091,-0.063],[0,0],[-0.282,0],[-0.276,0.395],[0.644,0.449],[0,0],[0,0],[0.035,0.064]],"o":[[0.074,0.082],[0,0],[0.247,0.175],[0.448,0],[0.447,-0.644],[0,0],[0,0],[-0.291,-1.581],[-4.465,2.376]],"v":[[4.208,-6.719],[4.455,-6.499],[8.738,-3.549],[9.55,-3.292],[10.717,-3.899],[10.366,-5.878],[6.496,-8.672],[4.938,-13.658],[1.653,-16.626]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.313,-10.375],"ix":2},"a":{"a":0,"k":[1.313,-15.375],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":10.161,"s":[40],"e":[78]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":25.402,"s":[78],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":35.562,"s":[40],"e":[0]},{"t":50.8035049438477}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10.161,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.321,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":25.402,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":35.562,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":45.723,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"t":50.8035049438477}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.063,2.75],"ix":2},"a":{"a":0,"k":[-0.063,-2.25],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-42]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":10.161,"s":[-42],"e":[-57]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20.321,"s":[-57],"e":[-59]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":25.402,"s":[-59],"e":[-25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":35.562,"s":[-25],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45.723,"s":[4],"e":[0]},{"t":50.8035049438477}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":10.161,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.321,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":25.402,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":35.562,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":45.723,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"t":50.8035049438477}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.094,3.281],"ix":2},"a":{"a":0,"k":[1.094,-1.719],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[34]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":10.161,"s":[34],"e":[64]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20.321,"s":[64],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":25.402,"s":[60],"e":[18]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":35.562,"s":[18],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45.723,"s":[4],"e":[0]},{"t":50.8035049438477}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.013,0.152],[0,0],[-0.091,-0.063],[1.678,0.136],[0.55,-0.255],[0.162,-0.111],[0,0],[0,0],[-0.022,-0.203],[-1.843,0.062]],"o":[[0.034,-0.145],[0,0],[0.074,0.082],[0,0],[-0.65,-0.052],[-0.175,0.062],[0,0],[0,0],[-0.018,0.211],[0,0],[1.843,-0.062]],"v":[[3.59,-2.105],[3.664,-2.552],[4.208,-6.719],[4.455,-6.499],[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-2.379,-10.24],[-2.626,-3.062],[-2.616,-2.439],[0.782,2.375]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":50.8035049438477,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[99.657,107.486,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.625,-20.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.017,0.019],[0.04,0.027],[0,0],[0,0],[0,0],[-0.672,0],[-0.124,0.03],[0.205,0.745],[0,0],[0.348,0.264],[0,0]],"o":[[-0.013,0.013],[0,0],[0,0],[0,0],[0.173,0.629],[0.123,0],[0.807,-0.189],[0,0],[-0.11,-0.401],[0,0],[0,0]],"v":[[2.561,-4.414],[0.991,-2.508],[2.518,0.539],[9.146,5.668],[11.642,14.735],[13.1,15.782],[13.472,15.738],[14.561,14.047],[13.08,4.085],[12.375,3.061],[5.551,-3.128]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.063,-2.188],"ix":2},"a":{"a":0,"k":[3.063,-2.188],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":81.286,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":94.833,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108.381,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":115.155,"s":[0],"e":[94]},{"t":121.928411865234}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.039,-0.055],[0,0],[0,0],[0.064,-0.838],[-0.814,-0.068],[0,0],[-0.055,0],[-0.387,0.536],[0,0],[0.941,1.069]],"o":[[-0.043,0.051],[0,0],[0,0],[-0.815,-0.069],[-0.066,0.839],[0,0],[0.057,0.004],[0.69,0],[0,0],[0,0],[-1.011,-1.15]],"v":[[-0.697,-4.047],[-0.822,-3.891],[-5.511,3.443],[-13.571,3.263],[-15.162,4.658],[-13.808,6.299],[-4.627,7.337],[-4.461,7.344],[-2.736,6.49],[2.518,0.539],[3.114,-3.753]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-3.313],"ix":2},"a":{"a":0,"k":[2,-3.313],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":81.286,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":94.833,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108.381,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":115.155,"s":[0],"e":[-92]},{"t":121.928411865234}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.576,0.03],[-0.027,0.003],[-0.261,0.549],[0,0],[0.723,0.342],[0.341,-0.722],[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.218,-1.676],[0,0]],"o":[[0.348,0.465],[0.027,-0.002],[0.604,-0.061],[0,0],[0.343,-0.722],[-0.722,-0.341],[0,0],[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.956,-0.079],[-0.218,1.676],[0,0]],"v":[[8.772,-5.613],[10.252,-4.92],[10.334,-4.927],[11.728,-5.911],[14.885,-10.833],[14.198,-12.759],[12.272,-12.071],[10.205,-9.296],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[2.522,-12.203],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.5,-14.094],"ix":2},"a":{"a":0,"k":[3.5,-14.094],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[0],"e":[102]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":81.286,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":94.833,"s":[0],"e":[102]},{"t":108.380810546875}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.571,1.598],[0,0],[0,0],[0.003,0],[0.309,-0.694],[0,0],[-0.727,-0.324],[-0.142,-0.016],[-0.263,0.59],[0,0],[0,0]],"o":[[0.571,-1.598],[0,0],[-0.002,0],[-0.752,-0.087],[0,0],[-0.324,0.727],[0.138,0.061],[0.61,0.071],[0,0],[0,0],[0,0]],"v":[[4.577,-12.524],[3.928,-15.382],[-2.871,-15.695],[-2.877,-15.695],[-4.66,-14.677],[-6.655,-9.525],[-5.924,-7.621],[-5.502,-7.506],[-4.02,-8.352],[-2.009,-12.1],[0.749,-11.524]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.656,-13.281],"ix":2},"a":{"a":0,"k":[4.656,-13.281],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[0],"e":[-146]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":81.286,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":94.833,"s":[0],"e":[-146]},{"t":108.380810546875}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.237,0.013],[0.018,0.003],[0,0],[1.075,-1.386],[0,0],[0,0],[0.039,-0.055],[0,0],[0,0],[0.348,0.264],[0,0],[0,0]],"o":[[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.238,-0.059],[-0.016,-0.002],[0,0],[0,0],[-1.075,1.386],[0,0],[-0.043,0.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.798,-10.046],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[4.795,-15.336],[4.746,-15.345],[3.928,-15.382],[1.879,-14.538],[0.749,-11.524],[-0.697,-4.047],[-0.822,-3.891],[-0.403,-0.141],[2.518,0.539],[5.47,-0.404],[5.551,-3.128],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":4,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":54.1904052734375,"op":108.380810546875,"st":54.1904052734375,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[99.657,107.486,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.625,-20.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.017,0.019],[0.04,0.027],[0,0],[0,0],[0,0],[-0.672,0],[-0.124,0.03],[0.205,0.745],[0,0],[0.348,0.264],[0,0]],"o":[[-0.013,0.013],[0,0],[0,0],[0,0],[0.173,0.629],[0.123,0],[0.807,-0.189],[0,0],[-0.11,-0.401],[0,0],[0,0]],"v":[[2.561,-4.414],[0.991,-2.508],[2.518,0.539],[9.146,5.668],[11.642,14.735],[13.1,15.782],[13.472,15.738],[14.561,14.047],[13.08,4.085],[12.375,3.061],[5.551,-3.128]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.063,-2.188],"ix":2},"a":{"a":0,"k":[3.063,-2.188],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13.548,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":27.095,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":40.643,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60.964,"s":[0],"e":[94]},{"t":67.7380065917969}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.039,-0.055],[0,0],[0,0],[0.064,-0.838],[-0.814,-0.068],[0,0],[-0.055,0],[-0.387,0.536],[0,0],[0.941,1.069]],"o":[[-0.043,0.051],[0,0],[0,0],[-0.815,-0.069],[-0.066,0.839],[0,0],[0.057,0.004],[0.69,0],[0,0],[0,0],[-1.011,-1.15]],"v":[[-0.697,-4.047],[-0.822,-3.891],[-5.511,3.443],[-13.571,3.263],[-15.162,4.658],[-13.808,6.299],[-4.627,7.337],[-4.461,7.344],[-2.736,6.49],[2.518,0.539],[3.114,-3.753]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-3.313],"ix":2},"a":{"a":0,"k":[2,-3.313],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13.548,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":27.095,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":40.643,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60.964,"s":[0],"e":[-92]},{"t":67.7380065917969}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.576,0.03],[-0.027,0.003],[-0.261,0.549],[0,0],[0.723,0.342],[0.341,-0.722],[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.218,-1.676],[0,0]],"o":[[0.348,0.465],[0.027,-0.002],[0.604,-0.061],[0,0],[0.343,-0.722],[-0.722,-0.341],[0,0],[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.956,-0.079],[-0.218,1.676],[0,0]],"v":[[8.772,-5.613],[10.252,-4.92],[10.334,-4.927],[11.728,-5.911],[14.885,-10.833],[14.198,-12.759],[12.272,-12.071],[10.205,-9.296],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[2.522,-12.203],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.5,-14.094],"ix":2},"a":{"a":0,"k":[3.5,-14.094],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13.548,"s":[0],"e":[102]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":27.095,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":40.643,"s":[0],"e":[102]},{"t":54.1904052734375}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.571,1.598],[0,0],[0,0],[0.003,0],[0.309,-0.694],[0,0],[-0.727,-0.324],[-0.142,-0.016],[-0.263,0.59],[0,0],[0,0]],"o":[[0.571,-1.598],[0,0],[-0.002,0],[-0.752,-0.087],[0,0],[-0.324,0.727],[0.138,0.061],[0.61,0.071],[0,0],[0,0],[0,0]],"v":[[4.577,-12.524],[3.928,-15.382],[-2.871,-15.695],[-2.877,-15.695],[-4.66,-14.677],[-6.655,-9.525],[-5.924,-7.621],[-5.502,-7.506],[-4.02,-8.352],[-2.009,-12.1],[0.749,-11.524]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.656,-13.281],"ix":2},"a":{"a":0,"k":[4.656,-13.281],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13.548,"s":[0],"e":[-146]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":27.095,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":40.643,"s":[0],"e":[-146]},{"t":54.1904052734375}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.237,0.013],[0.018,0.003],[0,0],[1.075,-1.386],[0,0],[0,0],[0.039,-0.055],[0,0],[0,0],[0.348,0.264],[0,0],[0,0]],"o":[[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.238,-0.059],[-0.016,-0.002],[0,0],[0,0],[-1.075,1.386],[0,0],[-0.043,0.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.798,-10.046],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[4.795,-15.336],[4.746,-15.345],[3.928,-15.382],[1.879,-14.538],[0.749,-11.524],[-0.697,-4.047],[-0.822,-3.891],[-0.403,-0.141],[2.518,0.539],[5.47,-0.404],[5.551,-3.128],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":4,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":54.1904052734375,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-14.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":67.738,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84.673,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"t":101.607009887695}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.188,-6.875],"ix":2},"a":{"a":0,"k":[-2.188,-9.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[-90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":84.673,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":88.059,"s":[0],"e":[-90]},{"t":101.607009887695}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":67.738,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84.673,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"t":101.607009887695}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-6.969],"ix":2},"a":{"a":0,"k":[2,-9.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":84.673,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":88.059,"s":[0],"e":[90]},{"t":101.607009887695}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":81.286,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":88.059,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}]},{"t":101.607009887695}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[-30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":84.673,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":88.059,"s":[0],"e":[-30]},{"t":101.607009887695}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":81.286,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":88.059,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}]},{"t":101.607009887695}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":84.673,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":88.059,"s":[0],"e":[30]},{"t":101.607009887695}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            2,\n            3,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.551,0.153],[0,0],[0,0],[-3.568,0.081],[0,0],[0,0]],"o":[[-0.548,0.165],[0,0],[0,0],[0,0],[3.568,-0.081],[0,0],[0.001,-0.048]],"v":[[1.526,-11.742],[-1.779,-11.724],[-3.864,-8.282],[-3.606,0.078],[-0.006,4.393],[3.587,0.078],[4.117,-8.282]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":67.7380065917969,"op":101.607009887695,"st":67.7380065917969,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-14.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":33.869,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":50.804,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"t":67.7380065917969}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.188,-6.875],"ix":2},"a":{"a":0,"k":[-2.188,-9.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33.869,"s":[-90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[0],"e":[-90]},{"t":67.7380065917969}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":33.869,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":50.804,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"t":67.7380065917969}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-6.969],"ix":2},"a":{"a":0,"k":[2,-9.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33.869,"s":[90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[0],"e":[90]},{"t":67.7380065917969}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":47.417,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":54.19,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}]},{"t":67.7380065917969}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33.869,"s":[-30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[0],"e":[-30]},{"t":67.7380065917969}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":47.417,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":54.19,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}]},{"t":67.7380065917969}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33.869,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":50.804,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[0],"e":[30]},{"t":67.7380065917969}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            2,\n            3,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.551,0.153],[0,0],[0,0],[-3.568,0.081],[0,0],[0,0]],"o":[[-0.548,0.165],[0,0],[0,0],[0,0],[3.568,-0.081],[0,0],[0.001,-0.048]],"v":[[1.526,-11.742],[-1.779,-11.724],[-3.864,-8.282],[-3.606,0.078],[-0.006,4.393],[3.587,0.078],[4.117,-8.282]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":33.8690032958984,"op":67.7380065917969,"st":33.8690032958984,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-14.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.935,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"t":33.8690032958984}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.188,-6.875],"ix":2},"a":{"a":0,"k":[-2.188,-9.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16.935,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20.321,"s":[0],"e":[-90]},{"t":33.8690032958984}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.935,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"t":33.8690032958984}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-6.969],"ix":2},"a":{"a":0,"k":[2,-9.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16.935,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20.321,"s":[0],"e":[90]},{"t":33.8690032958984}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13.548,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.321,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}]},{"t":33.8690032958984}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16.935,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20.321,"s":[0],"e":[-30]},{"t":33.8690032958984}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13.548,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20.321,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}]},{"t":33.8690032958984}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16.935,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":20.321,"s":[0],"e":[30]},{"t":33.8690032958984}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            2,\n            3,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1, val2, val2, val3;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.551,0.153],[0,0],[0,0],[-3.568,0.081],[0,0],[0,0]],"o":[[-0.548,0.165],[0,0],[0,0],[0,0],[3.568,-0.081],[0,0],[0.001,-0.048]],"v":[[1.526,-11.742],[-1.779,-11.724],[-3.864,-8.282],[-3.606,0.078],[-0.006,4.393],[3.587,0.078],[4.117,-8.282]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":33.8690032958984,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[101.25,104.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.74,0.359],[0.357,1.738],[1.738,-0.356],[-0.354,-1.737]],"o":[[1.733,-0.354],[-0.355,-1.737],[-1.738,0.354],[0.357,1.737]],"v":[[0.647,3.143],[3.144,-0.646],[-0.645,-3.146],[-3.147,0.646]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-11.25,4.844],"ix":2},"a":{"a":0,"k":[6.5,17.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16.935,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42.336,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":59.271,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":84.673,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96.527,"s":[30],"e":[0]},{"t":111.767710876465}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.145,-0.018],[0.306,-0.102],[-0.519,-1.549],[-0.175,-1.679],[-3.71,1.418],[0,0],[-0.004,0.004],[0,0],[0.369,1.397]],"o":[[-0.14,-0.014],[-0.313,0],[-1.549,0.52],[0.694,2.07],[0.146,0.847],[0,0],[0.003,-0.002],[0,0],[-0.506,-1.975],[0,0]],"v":[[-14.642,-8.556],[-15.072,-8.547],[-16.006,-8.398],[-17.872,-4.654],[-15.683,2.599],[-11.015,5.502],[-11.014,5.502],[-11.004,5.496],[-10.372,0.328],[-11.79,-5.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-11.266,4.844],"ix":2},"a":{"a":0,"k":[-11.266,4.844],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":16.935,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42.336,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":59.271,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":84.673,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96.527,"s":[30],"e":[0]},{"t":111.767710876465}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}],"e":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16.935,"s":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}],"e":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":42.336,"s":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}],"e":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":59.271,"s":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}],"e":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84.673,"s":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}],"e":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":96.527,"s":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}],"e":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}]},{"t":111.767710876465}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0.113,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.71,1.418],[0,0],[-0.004,0.004],[0,0],[0,0],[-0.291,0.617],[0.617,0.289],[0,0],[0.512,-0.252],[0,0]],"o":[[0,0],[0.003,-0.002],[0,0],[0,0],[0.616,0.29],[0.29,-0.617],[0,0],[-0.515,-0.242],[0,0],[-0.506,-1.975]],"v":[[-11.015,5.502],[-11.014,5.502],[-11.004,5.496],[-3.568,1.344],[3.58,4.77],[5.222,4.178],[4.63,2.535],[-2.965,-2.291],[-4.588,-2.274],[-10.372,0.328]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,2.914],[2.615,0.41],[0.044,0.006],[0,0],[0,-2.097],[-0.673,-0.908],[0,0],[0,-0.611],[-0.612,0],[0,0],[-0.006,0],[-0.006,0],[0,0],[-0.015,0.009]],"o":[[0,-2.729],[-0.03,-0.013],[0,0],[-1.733,0.917],[0,1.218],[0,0],[-0.612,0],[0,0.613],[0,0],[0,0],[0,0],[0,0],[0.059,0],[2.874,-0.162]],"v":[[19.065,3.104],[14.448,-2.291],[14.337,-2.32],[11.048,-1.729],[8.133,3.104],[9.208,6.351],[-17.956,6.351],[-19.065,7.459],[-17.956,8.568],[13.58,8.568],[13.599,8.569],[13.618,8.568],[13.808,8.568],[13.91,8.554]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[2.034,0],[0,2.034],[-2.034,0],[0,-2.034]],"o":[[-2.034,0],[0,-2.034],[2.034,0],[0,2.034]],"v":[[13.599,6.787],[9.916,3.104],[13.599,-0.58],[17.282,3.104]],"c":true},"ix":2},"nm":"circle","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"macine","np":3,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":338.690032958984,"st":0,"bm":0}]},{"id":"comp_4","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.539,100.996,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.473,-1.118],[-1.989,-1.65],[-1.284,0.567],[0.251,1.628]],"o":[[-1.342,-1.164],[-1.927,1.461],[1.08,0.896],[1.73,-0.764],[0,0]],"v":[[-11.176,-7.404],[-16.007,-7.559],[-16.101,-1.52],[-12.186,-0.974],[-9.898,-5.095]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.438,6.953],"ix":2},"a":{"a":0,"k":[3.438,6.953],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":11.854,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":27.095,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":38.949,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":66.045,"s":[25],"e":[0]},{"t":81.2856079101562}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0.409,0.451],[0.604,-0.063],[0,0],[0.251,1.628],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.215,-1.029],[-3.595,0],[0,0],[-1.66,1.26]],"o":[[0,0],[0,0],[0,0],[0.121,-0.596],[-0.409,-0.449],[0,0],[-1.342,-1.164],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.983,1.252],[2.743,2.324],[0,0],[2.084,0],[0,0]],"v":[[1.707,2.891],[-2.509,2.815],[-5.914,0.3],[-3.283,-6.308],[-3.714,-8.037],[-5.326,-8.653],[-11.176,-7.404],[-9.898,-5.095],[-6.652,-5.636],[-6.762,-5.477],[-6.747,-5.513],[-9.383,-1.929],[-11.629,1.03],[-11.216,5.116],[-1.402,8.716],[-1.002,8.716],[4.769,6.774]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.453,6.953],"ix":2},"a":{"a":0,"k":[3.453,6.953],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":11.854,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":27.095,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":38.949,"s":[25],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":54.19,"s":[0],"e":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":66.045,"s":[25],"e":[0]},{"t":81.2856079101562}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body top","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.73,0.716],[0,0],[1.121,-0.761],[0,0],[0,0],[0,0],[-1.66,1.26],[0,0],[0,0],[-0.883,0.883]],"o":[[0,0],[-0.961,-0.956],[0,0],[0,0],[0,0],[2.084,0],[0,0],[0,0],[0.341,0.247],[0.679,-0.678]],"v":[[17.34,5.004],[11.346,-1.059],[7.762,-1.394],[1.707,2.891],[-2.509,2.815],[0.06,6.434],[4.769,6.774],[9.442,2.983],[14.991,7.558],[17.412,7.557]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body bottom","np":3,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":338.690032958984,"st":0,"bm":0}]},{"id":"comp_5","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.027,-1.297],[-1.295,2.036],[2.03,1.294],[1.295,-2.031]],"o":[[2.029,1.293],[1.292,-2.024],[-2.029,-1.297],[-1.298,2.028]],"v":[[-2.531,15.115],[3.49,13.779],[2.159,7.764],[-3.859,9.094]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,-3.063],"e":[0,-5.063],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":25.402,"s":[0,-5.063],"e":[0,-3.063],"to":[0,0],"ti":[0,0]},{"t":67.7380065917969}],"ix":2},"a":{"a":0,"k":[-0.188,20.375],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":67.738,"s":[0],"e":[35]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":101.607,"s":[35],"e":[-35]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":169.345,"s":[-35],"e":[0]},{"t":203.214019775391}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[8.091,-0.689],[5.568,-8.097],[5.265,-8.719],[0.277,-11.541],[0.275,-11.541],[-4.753,-8.612],[-4.978,-8.113],[-7.582,-0.54],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}],"e":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[9.091,-0.876],[7.725,-9.097],[7.421,-9.719],[0.09,-13.604],[0.088,-13.604],[-7.472,-9.706],[-7.696,-9.207],[-8.988,-0.759],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":25.402,"s":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[9.091,-0.876],[7.725,-9.097],[7.421,-9.719],[0.09,-13.604],[0.088,-13.604],[-7.472,-9.706],[-7.696,-9.207],[-8.988,-0.759],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}],"e":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[8.091,-0.689],[5.568,-8.097],[5.265,-8.719],[0.277,-11.541],[0.275,-11.541],[-4.753,-8.612],[-4.978,-8.113],[-7.582,-0.54],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}]},{"t":67.7380065917969}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,6],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":338.690032958984,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"walking","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[80,113,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[40,40,100],"ix":6}},"ao":0,"w":200,"h":200,"ip":0,"op":338.690032958984,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"running","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[182.5,115,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[40,40,100],"ix":6}},"ao":0,"w":200,"h":200,"ip":0,"op":338.690032958984,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"star jumps","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84.5,213,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[40,40,100],"ix":6}},"ao":0,"w":200,"h":200,"ip":0,"op":338.690032958984,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"rawing machine","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[191.5,222.5,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[40,40,100],"ix":6}},"ao":0,"w":200,"h":200,"ip":0,"op":338.690032958984,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"crunches","refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[193,313.5,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[40,40,100],"ix":6}},"ao":0,"w":200,"h":200,"ip":0,"op":338.690032958984,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"yoga","refId":"comp_5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81.5,314.5,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[40,40,100],"ix":6}},"ao":0,"w":200,"h":200,"ip":0,"op":338.690032958984,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/mask.json b/example/resource/mask.json
new file mode 100755 (executable)
index 0000000..29437b7
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":0,"op":30,"w":500,"h":500,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Red Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[100,100,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"s","pt":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[27.614,0],[0,-27.614],[-27.614,0],[0,27.614]],"o":[[-27.614,0],[0,27.614],[27.614,0],[0,-27.614]],"v":[[100,50],[50,100],[100,150],[150,100]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[50,50],[50,150],[150,150],[150,50]],"c":true}]},{"t":29}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":200,"sh":200,"sc":"#ea0c11","ip":0,"op":30,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/material_wave_loading.json b/example/resource/material_wave_loading.json
new file mode 100644 (file)
index 0000000..cafcccf
--- /dev/null
@@ -0,0 +1 @@
+{"v":"4.6.8","fr":29.9700012207031,"ip":0,"op":40.0000016292334,"w":256,"h":256,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":20,"s":[208.6,127.969,0],"e":[208.6,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":30,"s":[208.6,88,0],"e":[208.6,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":40.0000016292334}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.9843137,0.5490196,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":15,"s":[168.6,128,0],"e":[168.6,88,0],"to":[0,-6.66666650772095,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":25,"s":[168.6,88,0],"e":[168.6,128,0],"to":[0,0,0],"ti":[0,-6.66666650772095,0]},{"t":35.0000014255792}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.9921569,0.8470588,0.2078431,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":10,"s":[128.594,127.969,0],"e":[128.594,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":20,"s":[128.594,88,0],"e":[128.594,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":30.0000012219251}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.2627451,0.627451,0.2784314,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":5,"s":[88.6,127.969,0],"e":[88.6,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":15,"s":[88.6,88,0],"e":[88.6,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":25.0000010182709}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.1176471,0.5333334,0.8980392,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":0,"s":[48.6,127.969,0],"e":[48.6,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":10,"s":[48.6,88,0],"e":[48.6,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":20.0000008146167}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.8980392,0.2235294,0.2078431,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1}]}
\ No newline at end of file
diff --git a/example/resource/polystar_star_with_roundness_points_stroke_99.json b/example/resource/polystar_star_with_roundness_points_stroke_99.json
new file mode 100755 (executable)
index 0000000..24a82d4
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.1.17","fr":29.9700012207031,"ip":0,"op":150.000006109625,"w":300,"h":300,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[152,152,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"sr","sy":1,"d":2,"pt":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[5],"e":[11]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[11],"e":[99]},{"t":100.000004073084}],"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[64]},{"t":90.0000036657751}],"ix":5},"ir":{"a":0,"k":75,"ix":6},"is":{"a":0,"k":79,"ix":8},"or":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[102],"e":[142]},{"t":90.0000036657751}],"ix":7},"os":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[79],"e":[247]},{"t":90.0000036657751}],"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 2","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":150.000006109625,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/rowing_machine.json b/example/resource/rowing_machine.json
new file mode 100755 (executable)
index 0000000..f6a814c
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":0,"op":151,"w":200,"h":200,"nm":"rawing machine","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[101.25,104.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.74,0.359],[0.357,1.738],[1.738,-0.356],[-0.354,-1.737]],"o":[[1.733,-0.354],[-0.355,-1.737],[-1.738,0.354],[0.357,1.737]],"v":[[0.647,3.143],[3.144,-0.646],[-0.645,-3.146],[-3.147,0.646]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-11.25,4.844],"ix":2},"a":{"a":0,"k":[6.5,17.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":75,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":105,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":150,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":171,"s":[30],"e":[0]},{"t":198}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.145,-0.018],[0.306,-0.102],[-0.519,-1.549],[-0.175,-1.679],[-3.71,1.418],[0,0],[-0.004,0.004],[0,0],[0.369,1.397]],"o":[[-0.14,-0.014],[-0.313,0],[-1.549,0.52],[0.694,2.07],[0.146,0.847],[0,0],[0.003,-0.002],[0,0],[-0.506,-1.975],[0,0]],"v":[[-14.642,-8.556],[-15.072,-8.547],[-16.006,-8.398],[-17.872,-4.654],[-15.683,2.599],[-11.015,5.502],[-11.014,5.502],[-11.004,5.496],[-10.372,0.328],[-11.79,-5.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-11.266,4.844],"ix":2},"a":{"a":0,"k":[-11.266,4.844],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":75,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":105,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":150,"s":[0],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":171,"s":[30],"e":[0]},{"t":198}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}],"e":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":30,"s":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}],"e":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":75,"s":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}],"e":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":105,"s":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}],"e":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":150,"s":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}],"e":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":171,"s":[{"i":[[2.142,0.411],[0.036,0.006],[0,0],[0.343,0.042],[0,0],[0.303,1.398],[0,0],[-0.165,0.143],[0,0]],"o":[[-0.025,-0.013],[0,0],[-0.108,-0.38],[0,0],[-0.114,-0.014],[0,0],[0.218,0.026],[0,0],[-1.419,0.916]],"v":[[14.921,-2.292],[14.831,-2.321],[0.384,-6.112],[-0.347,-6.825],[-8.903,-8.567],[-6.565,-5.262],[-0.52,-4.715],[0.067,-4.908],[12.138,-1.732]],"c":true}],"e":[{"i":[[2.615,0.41],[0.044,0.006],[0,0],[0.419,0.042],[0,0],[0.369,1.397],[0,0],[-0.201,0.143],[0,0]],"o":[[-0.03,-0.013],[0,0],[-0.132,-0.38],[0,0],[-0.14,-0.014],[0,0],[0.266,0.026],[0,0],[-1.733,0.917]],"v":[[14.448,-2.291],[14.337,-2.32],[-3.302,-6.104],[-4.195,-6.817],[-14.642,-8.556],[-11.79,-5.252],[-4.408,-4.707],[-3.691,-4.9],[11.048,-1.729]],"c":true}]},{"t":198}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0.113,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.71,1.418],[0,0],[-0.004,0.004],[0,0],[0,0],[-0.291,0.617],[0.617,0.289],[0,0],[0.512,-0.252],[0,0]],"o":[[0,0],[0.003,-0.002],[0,0],[0,0],[0.616,0.29],[0.29,-0.617],[0,0],[-0.515,-0.242],[0,0],[-0.506,-1.975]],"v":[[-11.015,5.502],[-11.014,5.502],[-11.004,5.496],[-3.568,1.344],[3.58,4.77],[5.222,4.178],[4.63,2.535],[-2.965,-2.291],[-4.588,-2.274],[-10.372,0.328]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,2.914],[2.615,0.41],[0.044,0.006],[0,0],[0,-2.097],[-0.673,-0.908],[0,0],[0,-0.611],[-0.612,0],[0,0],[-0.006,0],[-0.006,0],[0,0],[-0.015,0.009]],"o":[[0,-2.729],[-0.03,-0.013],[0,0],[-1.733,0.917],[0,1.218],[0,0],[-0.612,0],[0,0.613],[0,0],[0,0],[0,0],[0,0],[0.059,0],[2.874,-0.162]],"v":[[19.065,3.104],[14.448,-2.291],[14.337,-2.32],[11.048,-1.729],[8.133,3.104],[9.208,6.351],[-17.956,6.351],[-19.065,7.459],[-17.956,8.568],[13.58,8.568],[13.599,8.569],[13.618,8.568],[13.808,8.568],[13.91,8.554]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[2.034,0],[0,2.034],[-2.034,0],[0,-2.034]],"o":[[-2.034,0],[0,-2.034],[2.034,0],[0,2.034]],"v":[[13.599,6.787],[9.916,3.104],[13.599,-0.58],[17.282,3.104]],"c":true},"ix":2},"nm":"circle","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"macine","np":3,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/running.json b/example/resource/running.json
new file mode 100755 (executable)
index 0000000..3f7219a
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":0,"op":96,"w":200,"h":200,"nm":"running","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[99.657,107.486,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.625,-20.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.017,0.019],[0.04,0.027],[0,0],[0,0],[0,0],[-0.672,0],[-0.124,0.03],[0.205,0.745],[0,0],[0.348,0.264],[0,0]],"o":[[-0.013,0.013],[0,0],[0,0],[0,0],[0.173,0.629],[0.123,0],[0.807,-0.189],[0,0],[-0.11,-0.401],[0,0],[0,0]],"v":[[2.561,-4.414],[0.991,-2.508],[2.518,0.539],[9.146,5.668],[11.642,14.735],[13.1,15.782],[13.472,15.738],[14.561,14.047],[13.08,4.085],[12.375,3.061],[5.551,-3.128]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.063,-2.188],"ix":2},"a":{"a":0,"k":[3.063,-2.188],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":144,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":168,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":192,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":204,"s":[0],"e":[94]},{"t":216}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.039,-0.055],[0,0],[0,0],[0.064,-0.838],[-0.814,-0.068],[0,0],[-0.055,0],[-0.387,0.536],[0,0],[0.941,1.069]],"o":[[-0.043,0.051],[0,0],[0,0],[-0.815,-0.069],[-0.066,0.839],[0,0],[0.057,0.004],[0.69,0],[0,0],[0,0],[-1.011,-1.15]],"v":[[-0.697,-4.047],[-0.822,-3.891],[-5.511,3.443],[-13.571,3.263],[-15.162,4.658],[-13.808,6.299],[-4.627,7.337],[-4.461,7.344],[-2.736,6.49],[2.518,0.539],[3.114,-3.753]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-3.313],"ix":2},"a":{"a":0,"k":[2,-3.313],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":144,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":168,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":192,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":204,"s":[0],"e":[-92]},{"t":216}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.576,0.03],[-0.027,0.003],[-0.261,0.549],[0,0],[0.723,0.342],[0.341,-0.722],[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.218,-1.676],[0,0]],"o":[[0.348,0.465],[0.027,-0.002],[0.604,-0.061],[0,0],[0.343,-0.722],[-0.722,-0.341],[0,0],[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.956,-0.079],[-0.218,1.676],[0,0]],"v":[[8.772,-5.613],[10.252,-4.92],[10.334,-4.927],[11.728,-5.911],[14.885,-10.833],[14.198,-12.759],[12.272,-12.071],[10.205,-9.296],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[2.522,-12.203],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.5,-14.094],"ix":2},"a":{"a":0,"k":[3.5,-14.094],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[0],"e":[102]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":144,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":168,"s":[0],"e":[102]},{"t":192}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.571,1.598],[0,0],[0,0],[0.003,0],[0.309,-0.694],[0,0],[-0.727,-0.324],[-0.142,-0.016],[-0.263,0.59],[0,0],[0,0]],"o":[[0.571,-1.598],[0,0],[-0.002,0],[-0.752,-0.087],[0,0],[-0.324,0.727],[0.138,0.061],[0.61,0.071],[0,0],[0,0],[0,0]],"v":[[4.577,-12.524],[3.928,-15.382],[-2.871,-15.695],[-2.877,-15.695],[-4.66,-14.677],[-6.655,-9.525],[-5.924,-7.621],[-5.502,-7.506],[-4.02,-8.352],[-2.009,-12.1],[0.749,-11.524]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.656,-13.281],"ix":2},"a":{"a":0,"k":[4.656,-13.281],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[0],"e":[-146]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":144,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":168,"s":[0],"e":[-146]},{"t":192}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.237,0.013],[0.018,0.003],[0,0],[1.075,-1.386],[0,0],[0,0],[0.039,-0.055],[0,0],[0,0],[0.348,0.264],[0,0],[0,0]],"o":[[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.238,-0.059],[-0.016,-0.002],[0,0],[0,0],[-1.075,1.386],[0,0],[-0.043,0.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.798,-10.046],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[4.795,-15.336],[4.746,-15.345],[3.928,-15.382],[1.879,-14.538],[0.749,-11.524],[-0.697,-4.047],[-0.822,-3.891],[-0.403,-0.141],[2.518,0.539],[5.47,-0.404],[5.551,-3.128],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":4,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":96,"op":192,"st":96,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[99.657,107.486,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.625,-20.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.017,0.019],[0.04,0.027],[0,0],[0,0],[0,0],[-0.672,0],[-0.124,0.03],[0.205,0.745],[0,0],[0.348,0.264],[0,0]],"o":[[-0.013,0.013],[0,0],[0,0],[0,0],[0.173,0.629],[0.123,0],[0.807,-0.189],[0,0],[-0.11,-0.401],[0,0],[0,0]],"v":[[2.561,-4.414],[0.991,-2.508],[2.518,0.539],[9.146,5.668],[11.642,14.735],[13.1,15.782],[13.472,15.738],[14.561,14.047],[13.08,4.085],[12.375,3.061],[5.551,-3.128]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.063,-2.188],"ix":2},"a":{"a":0,"k":[3.063,-2.188],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":24,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":72,"s":[0],"e":[94]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[94],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108,"s":[0],"e":[94]},{"t":120}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.039,-0.055],[0,0],[0,0],[0.064,-0.838],[-0.814,-0.068],[0,0],[-0.055,0],[-0.387,0.536],[0,0],[0.941,1.069]],"o":[[-0.043,0.051],[0,0],[0,0],[-0.815,-0.069],[-0.066,0.839],[0,0],[0.057,0.004],[0.69,0],[0,0],[0,0],[-1.011,-1.15]],"v":[[-0.697,-4.047],[-0.822,-3.891],[-5.511,3.443],[-13.571,3.263],[-15.162,4.658],[-13.808,6.299],[-4.627,7.337],[-4.461,7.344],[-2.736,6.49],[2.518,0.539],[3.114,-3.753]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-3.313],"ix":2},"a":{"a":0,"k":[2,-3.313],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":24,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":72,"s":[0],"e":[-92]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[-92],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108,"s":[0],"e":[-92]},{"t":120}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.576,0.03],[-0.027,0.003],[-0.261,0.549],[0,0],[0.723,0.342],[0.341,-0.722],[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.218,-1.676],[0,0]],"o":[[0.348,0.465],[0.027,-0.002],[0.604,-0.061],[0,0],[0.343,-0.722],[-0.722,-0.341],[0,0],[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.956,-0.079],[-0.218,1.676],[0,0]],"v":[[8.772,-5.613],[10.252,-4.92],[10.334,-4.927],[11.728,-5.911],[14.885,-10.833],[14.198,-12.759],[12.272,-12.071],[10.205,-9.296],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[2.522,-12.203],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.5,-14.094],"ix":2},"a":{"a":0,"k":[3.5,-14.094],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":24,"s":[0],"e":[102]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48,"s":[102],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":72,"s":[0],"e":[102]},{"t":96}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.571,1.598],[0,0],[0,0],[0.003,0],[0.309,-0.694],[0,0],[-0.727,-0.324],[-0.142,-0.016],[-0.263,0.59],[0,0],[0,0]],"o":[[0.571,-1.598],[0,0],[-0.002,0],[-0.752,-0.087],[0,0],[-0.324,0.727],[0.138,0.061],[0.61,0.071],[0,0],[0,0],[0,0]],"v":[[4.577,-12.524],[3.928,-15.382],[-2.871,-15.695],[-2.877,-15.695],[-4.66,-14.677],[-6.655,-9.525],[-5.924,-7.621],[-5.502,-7.506],[-4.02,-8.352],[-2.009,-12.1],[0.749,-11.524]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.656,-13.281],"ix":2},"a":{"a":0,"k":[4.656,-13.281],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":24,"s":[0],"e":[-146]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48,"s":[-146],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":72,"s":[0],"e":[-146]},{"t":96}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.009,0.01],[1.037,0.254],[0.237,0.013],[0.018,0.003],[0,0],[1.075,-1.386],[0,0],[0,0],[0.039,-0.055],[0,0],[0,0],[0.348,0.264],[0,0],[0,0]],"o":[[0,0],[0,0],[-0.009,-0.009],[-0.483,-0.828],[-0.238,-0.059],[-0.016,-0.002],[0,0],[0,0],[-1.075,1.386],[0,0],[-0.043,0.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.798,-10.046],[8.014,-13.23],[7.877,-13.474],[7.851,-13.502],[5.51,-15.23],[4.795,-15.336],[4.746,-15.345],[3.928,-15.382],[1.879,-14.538],[0.749,-11.524],[-0.697,-4.047],[-0.822,-3.891],[-0.403,-0.141],[2.518,0.539],[5.47,-0.404],[5.551,-3.128],[7.043,-7.479]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":4,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":96,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/star_jumps.json b/example/resource/star_jumps.json
new file mode 100755 (executable)
index 0000000..82f7c56
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":1,"op":62,"w":200,"h":200,"nm":"star jumps","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-14.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":120,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":150,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"t":180}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.188,-6.875],"ix":2},"a":{"a":0,"k":[-2.188,-9.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[-90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":150,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":156,"s":[0],"e":[-90]},{"t":180}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":120,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":150,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"t":180}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-6.969],"ix":2},"a":{"a":0,"k":[2,-9.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":150,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":156,"s":[0],"e":[90]},{"t":180}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":144,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":156,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}]},{"t":180}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[-30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":150,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":156,"s":[0],"e":[-30]},{"t":180}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":144,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":156,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}]},{"t":180}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":150,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":156,"s":[0],"e":[30]},{"t":180}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            2,\n            3,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.551,0.153],[0,0],[0,0],[-3.568,0.081],[0,0],[0,0]],"o":[[-0.548,0.165],[0,0],[0,0],[0,0],[3.568,-0.081],[0,0],[0.001,-0.048]],"v":[[1.526,-11.742],[-1.779,-11.724],[-3.864,-8.282],[-3.606,0.078],[-0.006,4.393],[3.587,0.078],[4.117,-8.282]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-14.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":90,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"t":120}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.188,-6.875],"ix":2},"a":{"a":0,"k":[-2.188,-9.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[-90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[0],"e":[-90]},{"t":120}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":90,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"t":120}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-6.969],"ix":2},"a":{"a":0,"k":[2,-9.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[0],"e":[90]},{"t":120}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":96,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}]},{"t":120}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[-30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[0],"e":[-30]},{"t":120}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":84,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":96,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}]},{"t":120}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":96,"s":[0],"e":[30]},{"t":120}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            2,\n            3,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.551,0.153],[0,0],[0,0],[-3.568,0.081],[0,0],[0,0]],"o":[[-0.548,0.165],[0,0],[0,0],[0,0],[3.568,-0.081],[0,0],[0.001,-0.048]],"v":[[1.526,-11.742],[-1.779,-11.724],[-3.864,-8.282],[-3.606,0.078],[-0.006,4.393],[3.587,0.078],[4.117,-8.282]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-14.438],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":30,"s":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-11.599,-16.915],[-13.29,-16.358],[-12.733,-14.666],[-8.351,-10.775],[-3.864,-8.282]],"c":true}],"e":[{"i":[[0.717,2.38],[0,0],[0,0],[0.313,-0.621],[-0.53,-0.458],[0,0],[-0.011,0.063]],"o":[[0,0],[0,0],[-0.545,-0.365],[-0.314,0.621],[0,0],[0,0],[2.396,0.532]],"v":[[-0.717,-11.599],[-7.06,-13.364],[-13.286,-13.415],[-14.978,-12.858],[-14.421,-11.166],[-8.351,-10.775],[-3.864,-8.282]],"c":true}]},{"t":60}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.188,-6.875],"ix":2},"a":{"a":0,"k":[-2.188,-9.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[0],"e":[-90]},{"t":60}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":30,"s":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[12.733,-14.666],[13.29,-16.358],[11.599,-16.915],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}],"e":[{"i":[[0,0],[0,0],[0.314,0.621],[0.573,-0.371],[0,0],[-0.026,0.054],[-2.461,0.5]],"o":[[0,0],[0.607,-0.463],[-0.313,-0.621],[0,0],[0,0],[-1.183,2.773],[0.008,0]],"v":[[8.035,-10.775],[13.921,-11.416],[14.478,-13.108],[12.786,-13.665],[6.745,-13.364],[0.964,-11.617],[4.117,-8.282]],"c":true}]},{"t":60}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2,-6.969],"ix":2},"a":{"a":0,"k":[2,-9.969],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[90],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[0],"e":[90]},{"t":60}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":24,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":36,"s":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.297,6.224],[-12.443,12.673],[-12.148,14.513],[-11.974,14.621],[-10.309,14.219],[-6.026,7.497],[-0.006,4.393]],"c":true}],"e":[{"i":[[3.419,-0.047],[0,0],[0,0],[-0.589,-0.426],[-0.061,-0.031],[-0.387,0.532],[0,0],[0.006,-0.049]],"o":[[0.013,-0.047],[0,0],[-0.427,0.589],[0.056,0.041],[0.568,0.29],[0,0],[0,0],[0.975,-3.393]],"v":[[-3.606,0.078],[-9.234,8.474],[-12.881,15.048],[-12.586,16.888],[-12.411,16.996],[-10.746,16.594],[-5.964,9.747],[-0.006,4.393]],"c":true}]},{"t":60}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[0],"e":[-30]},{"t":60}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":24,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":36,"s":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[6.345,7.764],[10.36,14.219],[12.024,14.621],[12.199,14.513],[12.494,12.673],[9.415,6.441],[3.587,0.078]],"c":true}],"e":[{"i":[[-1.494,-2.799],[0,0],[0,0],[-0.568,0.29],[-0.056,0.041],[0.427,0.589],[0,0],[0.007,0.016]],"o":[[-0.025,-0.018],[0,0],[0.385,0.532],[0.061,-0.031],[0.589,-0.426],[0,0],[0,0],[-3.118,-0.234]],"v":[[-0.006,4.393],[5.97,9.514],[10.735,16.594],[12.399,16.996],[12.574,16.888],[12.869,15.048],[9.04,8.191],[3.587,0.078]],"c":true}]},{"t":60}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[30],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[0],"e":[30]},{"t":60}],"ix":6,"x":"var $bm_rt;\nvar easingPreset = [\n        [\n            3,\n            4,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            1,\n            2,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ],\n        [\n            2,\n            3,\n            [\n                'make_bezier_easing',\n                [\n                    0.25,\n                    0.46,\n                    0.45,\n                    1\n                ],\n                'Glide.Out',\n                false\n            ]\n        ]\n    ];\nfunction easingMaker() {\n    var t, d, newProgress, sX, eX, sY, eY, sZ, eZ, val1;\n    var n = 0;\n    if (numKeys > 0) {\n        n = nearestKey(time).index;\n        if (key(n).time > time) {\n            n--;\n        }\n    }\n    try {\n        var key1 = key(n);\n        var key2 = key(sum(n, 1));\n    } catch (e) {\n        return null;\n    }\n    var dim = 1;\n    try {\n        key(1)[1];\n        dim = 2;\n        key(1)[2];\n        dim = 3;\n    } catch (e) {\n    }\n    var expression = null;\n    for (var i = 0; i < easingPreset.length; ++i) {\n        if (easingPreset[i][0] <= n && easingPreset[i][1] >= n + 1) {\n            var expression = eval([easingPreset[i][2][0]][0]);\n            try {\n                expression = expression.apply({}, easingPreset[i][2][1].concat(easingPreset[i][2][3]));\n            } catch (e) {\n                expression = expression.apply({}, easingPreset[i][2][1]);\n            }\n            break;\n        }\n    }\n    if (!expression)\n        return null;\n    t = sub(time, key1.time);\n    d = sub(key2.time, key1.time);\n    if (expression.hasOwnProperty('curviosity') && expression.curviosity) {\n        newProgress = expression.executeProgress(div(t, d));\n        return thisProperty.valueAtTime(sum(key1.time, mul(d, newProgress)));\n    }\n    sX = key1[0];\n    eX = sub(key2[0], key1[0]);\n    if (dim >= 2) {\n        sY = key1[1];\n        eY = sub(key2[1], key1[1]);\n        if (dim >= 3) {\n            sZ = key1[2];\n            eZ = sub(key2[2], key1[2]);\n        }\n    }\n    if (time < key1.time || time > key2.time) {\n        return value;\n    } else {\n        val1 = expression.execute(t, sX, eX, d);\n        switch (dim) {\n        case 1:\n            return val1;\n        case 2:\n            val2 = expression.execute(t, sY, eY, d);\n            return [\n                val1,\n                val2\n            ];\n        case 3:\n            val2 = expression.execute(t, sY, eY, d);\n            val3 = expression.execute(t, sZ, eZ, d);\n            return [\n                val1,\n                val2,\n                val3\n            ];\n        default:\n            return null;\n        }\n    }\n}\n$bm_rt = easingMaker() || value;\nfunction sampleCurveY(t) {\n    return mul(sum(mul(sum(mul(this.ay, t), this.by), t), this.cy), t);\n}\nfunction sampleCurveX(t) {\n    return mul(sum(mul(sum(mul(this.ax, t), this.bx), t), this.cx), t);\n}\nfunction sampleCurveDerivativeX(t) {\n    return sum(mul(sum(mul(mul(3, this.ax), t), mul(2, this.bx)), t), this.cx);\n}\nfunction solveCurveX(x, epsilon) {\n    var t2, i, x2, d2, t0, t1;\n    for (t2 = x, i = 0; i < 8; i++) {\n        x2 = sub(sampleCurveX.call(this, t2), x);\n        if (Math.abs(x2) < epsilon)\n            return t2;\n        d2 = sampleCurveDerivativeX.call(this, t2);\n        if (Math.abs(d2) < 0.000001)\n            break;\n        t2 = sub(t2, div(x2, d2));\n    }\n    t0 = 0;\n    t1 = 1;\n    t2 = x;\n    if (t2 < t0)\n        return t0;\n    if (t2 > t1)\n        return t1;\n    while (t0 < t1) {\n        x2 = sampleCurveX.call(this, t2);\n        if (Math.abs(x2 - x) < epsilon)\n            return t2;\n        if (x > x2)\n            t0 = t2;\n        else\n            t1 = t2;\n        t2 = sum(mul(sub(t1, t0), 0.5), t0);\n    }\n    return t2;\n}\nfunction executeBezier(t, b, e, d) {\n    return sum(b, mul(e, sampleCurveY.call(this, solveCurveX.call(this, div(t, d), div(1, mul(200, d))))));\n}\nfunction executeBezierProgress(oldProgress) {\n    return sampleCurveY.call(this, solveCurveX.call(this, oldProgress, 1 / 200));\n}\nfunction make_bezier_easing(p1x, p1y, p2x, p2y, curviosity) {\n    this.cx = mul(3, p1x);\n    this.bx = sub(mul(3, sub(p2x, p1x)), this.cx);\n    this.ax = sub(sub(1, this.cx), this.bx);\n    this.cy = mul(3, p1y);\n    this.by = sub(mul(3, sub(p2y, p1y)), this.cy);\n    this.ay = sub(sub(1, this.cy), this.by);\n    this.curviosity = curviosity;\n    this.execute = executeBezier;\n    this.executeProgress = executeBezierProgress;\n    return this;\n}\nfunction executeElasticIn(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum($bm_neg(mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n}\nfunction executeElasticOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d) === 1)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    return sum(sum(mul(mul(a, Math.pow(2, mul(-10, t))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), c), b);\n}\nfunction executeElasticInOut(t, b, c, d) {\n    var s = 1.70158;\n    var p = 0;\n    var a = c;\n    if (t === 0)\n        return b;\n    if ((t /= d / 2) === 2)\n        return sum(b, c);\n    if (!p)\n        p = mul(d, 0.3 * 1.5);\n    if (a < Math.abs(c)) {\n        a = c;\n        s = div(p, 4);\n    } else\n        s = mul(div(p, mul(2, Math.PI)), Math.asin(1));\n    if (t < 1)\n        return sum(mul(-0.5, mul(mul(a, Math.pow(2, mul(10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p)))), b);\n    return sum(sum(mul(mul(mul(a, Math.pow(2, mul(-10, t -= 1))), Math.sin(div(mul(sub(mul(t, d), s), mul(2, Math.PI)), p))), 0.5), c), b);\n}\nfunction executeElasticInProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : -Math.pow(2, 10 * Progress - 10) * Math.sin((Progress * 10 - 10.75) * (2 * Math.PI / 3));\n}\nfunction executeElasticOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Math.pow(2, -10 * Progress) * Math.sin((Progress * 10 - 0.75) * (2 * Math.PI / 3)) + 1;\n}\nfunction executeElasticInOutProgress(Progress) {\n    return Progress === 0 ? 0 : Progress === 1 ? 1 : Progress < 0.5 ? -(Math.pow(2, 20 * Progress - 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5))) / 2 : Math.pow(2, -20 * Progress + 10) * Math.sin((20 * Progress - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1;\n}\nfunction make_elastic_easing_in(curviosity) {\n    this.execute = executeElasticIn;\n    this.executeProgress = executeElasticInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_out(curviosity) {\n    this.execute = executeElasticOut;\n    this.executeProgress = executeElasticOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_elastic_easing_in_out(curviosity) {\n    this.execute = executeElasticInOut;\n    this.executeProgress = executeElasticInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction executeBounceIn(t, b, c, d) {\n    return sum(sub(c, executeBounceOut(sub(d, t), 0, c, d)), b);\n}\nfunction executeBounceOut(t, b, c, d) {\n    if ((t /= d) < 1 / 2.75) {\n        return sum(mul(c, mul(mul(7.5625, t), t)), b);\n    } else if (t < 2 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 1.5 / 2.75), t), 0.75)), b);\n    } else if (t < 2.5 / 2.75) {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.25 / 2.75), t), 0.9375)), b);\n    } else {\n        return sum(mul(c, sum(mul(mul(7.5625, t -= 2.625 / 2.75), t), 0.984375)), b);\n    }\n}\nfunction executeBounceInOut(t, b, c, d) {\n    if (t < d / 2)\n        return sum(mul(executeBounceIn(mul(t, 2), 0, c, d), 0.5), b);\n    return sum(sum(mul(executeBounceOut(sub(mul(t, 2), d), 0, c, d), 0.5), mul(c, 0.5)), b);\n}\nfunction executeBounceInProgress(oldProgress) {\n    return sub(1, executeBounceOut(sub(1, oldProgress)));\n}\nfunction executeBounceOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2.75) {\n        return mul(mul(7.5625, oldProgress), oldProgress);\n    } else if (oldProgress < 2 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 1.5 / 2.75), oldProgress), 0.75);\n    } else if (oldProgress < 2.5 / 2.75) {\n        return sum(mul(mul(7.5625, oldProgress -= 2.25 / 2.75), oldProgress), 0.9375);\n    } else {\n        return sum(mul(mul(7.5625, oldProgress -= 2.625 / 2.75), oldProgress), 0.984375);\n    }\n}\nfunction executeBounceInOutProgress(oldProgress) {\n    if (oldProgress < 1 / 2)\n        return mul(executeBounceIn(mul(oldProgress, 2)), 0.5);\n    return sum(mul(executeBounceOut(sub(mul(oldProgress, 2), 1)), 0.5), 0.5);\n}\nfunction make_bounce_easing_in(curviosity) {\n    this.execute = executeBounceIn;\n    this.executeProgress = executeBounceInProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_out(curviosity) {\n    this.execute = executeBounceOut;\n    this.executeProgress = executeBounceOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}\nfunction make_bounce_easing_in_out(curviosity) {\n    this.execute = executeBounceInOut;\n    this.executeProgress = executeBounceInOutProgress;\n    this.curviosity = curviosity;\n    return this;\n}"},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.551,0.153],[0,0],[0,0],[-3.568,0.081],[0,0],[0,0]],"o":[[-0.548,0.165],[0,0],[0,0],[0,0],[3.568,-0.081],[0,0],[0.001,-0.048]],"v":[[1.526,-11.742],[-1.779,-11.724],[-3.864,-8.282],[-3.606,0.078],[-0.006,4.393],[3.587,0.078],[4.117,-8.282]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,3],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/walking.json b/example/resource/walking.json
new file mode 100755 (executable)
index 0000000..99e5d61
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":0,"op":90,"w":200,"h":200,"nm":"walking","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,102,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.438,-16.813],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.66,4.158],[0.55,-0.255],[0.162,-0.111],[0,0],[0.052,-0.433],[0,0],[-0.72,-0.085],[-0.053,0],[-0.079,0.669],[0,0],[0,0]],"o":[[-0.122,0.001],[-0.175,0.062],[0,0],[-0.358,0.248],[0,0],[-0.083,0.72],[0.053,0.006],[0.656,0],[0,0],[0,0],[0,0]],"v":[[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-6.499,-11.32],[-7.142,-10.25],[-7.54,-4.643],[-6.387,-3.183],[-6.23,-3.174],[-4.926,-4.334],[-4.207,-9.214],[-2.379,-10.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.219,-10.875],"ix":2},"a":{"a":0,"k":[1.219,-15.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108,"s":[-40],"e":[-71]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":135,"s":[-71],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":153,"s":[-40],"e":[0]},{"t":180}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.091,-0.063],[0,0],[-0.282,0],[-0.276,0.395],[0.644,0.449],[0,0],[0,0],[0.035,0.064]],"o":[[0.074,0.082],[0,0],[0.247,0.175],[0.448,0],[0.447,-0.644],[0,0],[0,0],[-0.291,-1.581],[-4.465,2.376]],"v":[[4.208,-6.719],[4.455,-6.499],[8.738,-3.549],[9.55,-3.292],[10.717,-3.899],[10.366,-5.878],[6.496,-8.672],[4.938,-13.658],[1.653,-16.626]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.313,-10.375],"ix":2},"a":{"a":0,"k":[1.313,-15.375],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108,"s":[40],"e":[78]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":135,"s":[78],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":153,"s":[40],"e":[0]},{"t":180}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":90,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":108,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":126,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":135,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":153,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":171,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"t":180}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.063,2.75],"ix":2},"a":{"a":0,"k":[-0.063,-2.25],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[-42]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108,"s":[-42],"e":[-57]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":126,"s":[-57],"e":[-59]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":135,"s":[-59],"e":[-25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":153,"s":[-25],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":171,"s":[4],"e":[0]},{"t":180}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":90,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":108,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":126,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":135,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":153,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":171,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"t":180}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.094,3.281],"ix":2},"a":{"a":0,"k":[1.094,-1.719],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":90,"s":[0],"e":[34]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":108,"s":[34],"e":[64]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":126,"s":[64],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":135,"s":[60],"e":[18]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":153,"s":[18],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":171,"s":[4],"e":[0]},{"t":180}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.013,0.152],[0,0],[-0.091,-0.063],[1.678,0.136],[0.55,-0.255],[0.162,-0.111],[0,0],[0,0],[-0.022,-0.203],[-1.843,0.062]],"o":[[0.034,-0.145],[0,0],[0.074,0.082],[0,0],[-0.65,-0.052],[-0.175,0.062],[0,0],[0,0],[-0.018,0.211],[0,0],[1.843,-0.062]],"v":[[3.59,-2.105],[3.664,-2.552],[4.208,-6.719],[4.455,-6.499],[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-2.379,-10.24],[-2.626,-3.062],[-2.616,-2.439],[0.782,2.375]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":690,"st":90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,102,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.083,-0.067],[-0.067,2.085],[2.085,0.067],[0.068,-2.084]],"o":[[2.084,0.068],[0.067,-2.084],[-2.083,-0.068],[-0.067,2.085]],"v":[[-0.123,3.772],[3.774,0.12],[0.122,-3.773],[-3.774,-0.124]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.438,-16.813],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.66,4.158],[0.55,-0.255],[0.162,-0.111],[0,0],[0.052,-0.433],[0,0],[-0.72,-0.085],[-0.053,0],[-0.079,0.669],[0,0],[0,0]],"o":[[-0.122,0.001],[-0.175,0.062],[0,0],[-0.358,0.248],[0,0],[-0.083,0.72],[0.053,0.006],[0.656,0],[0,0],[0,0],[0,0]],"v":[[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-6.499,-11.32],[-7.142,-10.25],[-7.54,-4.643],[-6.387,-3.183],[-6.23,-3.174],[-4.926,-4.334],[-4.207,-9.214],[-2.379,-10.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.219,-10.875],"ix":2},"a":{"a":0,"k":[1.219,-15.875],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":18,"s":[-40],"e":[-71]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[-71],"e":[-40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":63,"s":[-40],"e":[0]},{"t":90}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm right","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.091,-0.063],[0,0],[-0.282,0],[-0.276,0.395],[0.644,0.449],[0,0],[0,0],[0.035,0.064]],"o":[[0.074,0.082],[0,0],[0.247,0.175],[0.448,0],[0.447,-0.644],[0,0],[0,0],[-0.291,-1.581],[-4.465,2.376]],"v":[[4.208,-6.719],[4.455,-6.499],[8.738,-3.549],[9.55,-3.292],[10.717,-3.899],[10.366,-5.878],[6.496,-8.672],[4.938,-13.658],[1.653,-16.626]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.313,-10.375],"ix":2},"a":{"a":0,"k":[1.313,-15.375],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":18,"s":[40],"e":[78]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[78],"e":[40]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":63,"s":[40],"e":[0]},{"t":90}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"arm left","np":2,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-13.908,5.842],[-14.222,8.524],[-13.282,8.855],[-12.04,9.012],[-4.151,10.956],[-3.306,10.456],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":36,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":45,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":63,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-9.071,14.817],[-8.832,16.934],[-7.892,17.265],[-6.713,16.695],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":81,"s":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-8.141,13.802],[-7.902,15.92],[-6.961,16.251],[-5.783,15.681],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}],"e":[{"i":[[7.053,-0.748],[0,0],[0,0],[-0.65,-0.519],[-0.331,0],[-0.297,0.374],[0,0],[-0.065,0.158],[0,0]],"o":[[-0.072,-0.061],[0,0],[-0.519,0.651],[0.277,0.222],[0.441,0],[0,0],[0.106,-0.134],[0,0],[0,0]],"v":[[-2.616,-2.439],[-5.683,6.781],[-10.646,13.483],[-10.407,15.601],[-9.466,15.932],[-8.288,15.362],[-2.648,8.873],[-2.389,8.435],[0.782,2.375]],"c":true}]},{"t":90}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.063,2.75],"ix":2},"a":{"a":0,"k":[-0.063,-2.25],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[-42]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":18,"s":[-42],"e":[-57]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[-57],"e":[-59]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[-59],"e":[-25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":63,"s":[-25],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":81,"s":[4],"e":[0]},{"t":90}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg right","np":2,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":18,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":36,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.788,14.342],[11.253,15.428],[11.706,15.362],[12.724,13.443]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":45,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":63,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[-0.146,0.395]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.884,7.314],[9.099,6.616],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[1.131,14.888],[2.597,15.974],[3.049,15.908],[4.628,14.457]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":81,"s":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[9.722,13.827],[11.188,14.913],[11.64,14.846],[12.659,12.927]],"c":true}],"e":[{"i":[[0,0],[0.101,0.149],[0,0],[-0.007,0.021],[0,0],[0,0],[0,0],[-0.656,0],[-0.15,0.046],[0.248,0.813]],"o":[[-0.054,-0.172],[0,0],[0.034,-0.145],[-5.804,0.458],[0,0],[0,0],[0.201,0.661],[0.149,0],[0.811,-0.248],[0,0]],"v":[[8.566,6.553],[8.333,6.066],[3.59,-2.105],[3.664,-2.552],[0.782,2.375],[5.127,7.892],[7.725,15.592],[9.191,16.678],[9.643,16.612],[10.662,14.693]],"c":true}]},{"t":90}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1.094,3.281],"ix":2},"a":{"a":0,"k":[1.094,-1.719],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[34]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":18,"s":[34],"e":[64]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":36,"s":[64],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[60],"e":[18]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":63,"s":[18],"e":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":81,"s":[4],"e":[0]},{"t":90}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"leg left","np":2,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.013,0.152],[0,0],[-0.091,-0.063],[1.678,0.136],[0.55,-0.255],[0.162,-0.111],[0,0],[0,0],[-0.022,-0.203],[-1.843,0.062]],"o":[[0.034,-0.145],[0,0],[0.074,0.082],[0,0],[-0.65,-0.052],[-0.175,0.062],[0,0],[0,0],[-0.018,0.211],[0,0],[1.843,-0.062]],"v":[[3.59,-2.105],[3.664,-2.552],[4.208,-6.719],[4.455,-6.499],[1.653,-16.626],[-0.169,-16.3],[-0.678,-16.043],[-2.379,-10.24],[-2.626,-3.062],[-2.616,-2.439],[0.782,2.375]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/example/resource/yoga.json b/example/resource/yoga.json
new file mode 100755 (executable)
index 0000000..9003783
--- /dev/null
@@ -0,0 +1 @@
+{"v":"5.0.6","fr":60,"ip":0,"op":361,"w":200,"h":200,"nm":"yoga","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[400,400,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.027,-1.297],[-1.295,2.036],[2.03,1.294],[1.295,-2.031]],"o":[[2.029,1.293],[1.292,-2.024],[-2.029,-1.297],[-1.298,2.028]],"v":[[-2.531,15.115],[3.49,13.779],[2.159,7.764],[-3.859,9.094]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,-3.063],"e":[0,-5.063],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":45,"s":[0,-5.063],"e":[0,-3.063],"to":[0,0],"ti":[0,0]},{"t":120}],"ix":2},"a":{"a":0,"k":[-0.188,20.375],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":120,"s":[0],"e":[35]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":180,"s":[35],"e":[-35]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":300,"s":[-35],"e":[0]},{"t":360}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"head","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[8.091,-0.689],[5.568,-8.097],[5.265,-8.719],[0.277,-11.541],[0.275,-11.541],[-4.753,-8.612],[-4.978,-8.113],[-7.582,-0.54],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}],"e":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[9.091,-0.876],[7.725,-9.097],[7.421,-9.719],[0.09,-13.604],[0.088,-13.604],[-7.472,-9.706],[-7.696,-9.207],[-8.988,-0.759],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":45,"s":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[9.091,-0.876],[7.725,-9.097],[7.421,-9.719],[0.09,-13.604],[0.088,-13.604],[-7.472,-9.706],[-7.696,-9.207],[-8.988,-0.759],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}],"e":[{"i":[[1.024,0.29],[0,0],[-0.277,0.418],[0.714,0.476],[0,0],[0,0],[0.127,0.188],[2.398,0.001],[0,0],[0.529,-1.678],[0.058,-0.178],[0,0],[0,0],[-0.511,-0.763],[-0.319,-0.117],[0,0],[-0.182,-1.05],[-1.004,0],[-0.048,0.003],[0,0],[-0.039,0.005],[-3.297,-0.177],[0,0],[0,0],[-0.047,0],[-0.174,1.002]],"o":[[0,0],[0.465,-0.031],[0.478,-0.714],[0,0],[0,0],[-0.074,-0.227],[-0.58,-1.622],[0,0],[-2.451,0],[-0.091,0.155],[0,0],[0,0],[-0.764,0.509],[0.201,0.305],[0,0],[-1.026,0.29],[0.174,1.003],[0.047,0],[0,0],[0.041,-0.002],[0.33,-0.03],[0.006,0],[0,0],[0.048,0.003],[1.004,0],[0.183,-1.05]],"v":[[15.109,7.459],[13.649,7.047],[14.839,6.361],[14.409,4.207],[8.091,-0.689],[5.568,-8.097],[5.265,-8.719],[0.277,-11.541],[0.275,-11.541],[-4.753,-8.612],[-4.978,-8.113],[-7.582,-0.54],[-13.894,4.078],[-14.353,6.38],[-13.545,7.017],[-15.108,7.459],[-16.593,9.816],[-14.545,11.541],[-14.403,11.536],[-2.768,10.742],[-2.647,10.728],[2.75,10.741],[2.761,10.742],[14.401,11.536],[14.544,11.541],[16.592,9.816]],"c":true}]},{"t":120}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,6],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"body","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/inc/lottieplayer.h b/inc/lottieplayer.h
new file mode 100644 (file)
index 0000000..dfda152
--- /dev/null
@@ -0,0 +1,149 @@
+#ifndef LOTPLAYER_H
+#define LOTPLAYER_H
+
+#include <vector>
+#include <future>
+
+#ifdef _WIN32
+# ifdef LOT_BUILD
+#  ifdef DLL_EXPORT
+#   define LOT_EXPORT __declspec(dllexport)
+#  else
+#   define LOT_EXPORT
+#  endif
+# else
+#  define LOT_EXPORT __declspec(dllimport)
+# endif
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define LOT_EXPORT __attribute__ ((visibility("default")))
+#  else
+#   define LOT_EXPORT
+#  endif
+# else
+#  define LOT_EXPORT
+# endif
+#endif
+
+class LOTPlayerPrivate;
+class LOTNode;
+
+struct LOT_EXPORT LOTBuffer
+{
+    uint32_t *buffer;
+    int       width;
+    int       height;
+    int       bytesPerLine;
+    bool      clear;
+};
+
+class LOT_EXPORT LOTPlayer
+{
+public:
+    ~LOTPlayer();
+    LOTPlayer();
+
+    LOTPlayer(const char *filePath);
+
+    bool setFilePath(const char *filePath);
+
+    void setSize(int width, int height);
+    void size(int &width, int &height) const;
+
+    int frameRate() const;
+
+    void setFrameRate(int frameRate);
+
+    float playTime() const;
+
+    void seek(float pos);
+
+    const std::vector<LOTNode *>& renderList() const;
+
+    std::future<bool> render(float pos, const LOTBuffer &buffer);
+    bool renderSync(float pos, const LOTBuffer &buffer);
+
+public:
+    LOTPlayerPrivate         *d;
+};
+
+#define ChangeFlagNone  0x0000
+#define ChangeFlagPath  0x0001
+#define ChangeFlagPaint 0x0010
+#define ChangeFlagAll   (ChangeFlagPath & ChangeFlagPaint)
+
+class LOT_EXPORT LOTNode
+{
+public:
+    struct PathData {
+        const float *ptPtr;
+        int          ptCount;
+        const char  *elmPtr;
+        int          elmCount;
+    };
+    struct Color {
+        unsigned short r, g, b, a;
+    };
+
+    enum BrushType {
+        BrushSolid,
+        BrushGradient
+    };
+    enum FillRule {
+        EvenOdd,
+        Winding
+    };
+
+    enum JoinStyle {
+        MiterJoin,
+        BevelJoin,
+        RoundJoin
+    };
+
+    enum CapStyle {
+        FlatCap,
+        SquareCap,
+        RoundCap
+    };
+
+    struct Stroke {
+        bool        enable;
+        int         width;
+        CapStyle    cap;
+        JoinStyle   join;
+        int         meterLimit;
+        float      *dashArray;
+        int         dashArraySize;
+    };
+
+    struct Gradient {
+        enum Type {
+            Linear = 1,
+            Radial = 2
+        };
+        Gradient::Type type;
+        struct {
+            float x, y;
+        } start, end;
+        struct {
+            float x, y;
+        } center, focal;
+        float cradius;
+        float fradius;
+    };
+
+    ~LOTNode();
+    LOTNode();
+
+public:
+    int                     mFlag;
+    LOTNode::BrushType      mType;
+    FillRule                mFillRule;
+    PathData                mPath;
+    Color                   mColor;
+    Stroke                  mStroke;
+    Gradient                mGradient;
+};
+
+#endif // LOTPLAYER_H
diff --git a/inc/meson.build b/inc/meson.build
new file mode 100644 (file)
index 0000000..0b4bb41
--- /dev/null
@@ -0,0 +1 @@
+install_headers(['lottieplayer.h'])
diff --git a/meson.build b/meson.build
new file mode 100644 (file)
index 0000000..ba33397
--- /dev/null
@@ -0,0 +1,36 @@
+project('lottie-player library',
+        'cpp',
+        license : 'Apache')
+
+lottie_player_lib_version = '0.0.1'
+
+add_global_arguments('-DDEMO_DIR="@0@/example/resource/"'.format(meson.current_source_dir()), language : 'cpp')
+
+compiler_flags = ['-Wall','-std=c++11', '-fvisibility=hidden']
+
+if (build_machine.system() == 'linux')
+    compiler_flags += ['-pthread']
+    add_global_link_arguments('-pthread', language: 'cpp')
+endif
+
+add_global_arguments(compiler_flags, language: 'cpp')
+
+inc = include_directories('inc')
+
+subdir('inc')
+subdir('src')
+
+subdir('example')
+
+if get_option('test') == true
+   subdir('test')
+endif
+
+pkg_mod = import('pkgconfig')
+
+pkg_mod.generate( libraries   : lottie_player_lib,
+                  version     : lottie_player_lib_version,
+                  name        : 'liblottie-player',
+                  filebase    : 'lottie-player',
+                  description : 'A Library for rendering lottie files.'
+                )
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644 (file)
index 0000000..d8e5890
--- /dev/null
@@ -0,0 +1,14 @@
+option('test',
+   type: 'boolean',
+   value: false,
+   description: 'enable unit test')
+
+option('example',
+   type: 'boolean',
+   value: true,
+   description: 'enable example')
+
+option('text',
+   type: 'boolean',
+   value: false,
+   description: 'enable text module')
diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp
new file mode 100644 (file)
index 0000000..6b4b2a7
--- /dev/null
@@ -0,0 +1,1074 @@
+#include "lottieitem.h"
+#include"vbitmap.h"
+#include"vpainter.h"
+#include"vraster.h"
+#include"vdasher.h"
+#include <cmath>
+
+
+VDrawable::VDrawable():mFlag(DirtyState::All),
+                       mType(Type::Fill),
+                       mFillRule(FillRule::Winding)
+{
+    mStroke.dashArraySize = 0;
+    mStroke.cap = CapStyle::Round;
+    mStroke.join= JoinStyle::Round;
+    mStroke.meterLimit = 10;
+    mStroke.enable = false;
+}
+
+void VDrawable::preprocess()
+{
+    if (mFlag & (DirtyState::Path)) {
+        if (mStroke.enable) {
+            VPath newPath = mPath;
+            if (mStroke.dashArraySize) {
+                VDasher dasher(mStroke.dashArray, mStroke.dashArraySize);
+                newPath = dasher.dashed(mPath);
+            }
+            FTOutline *outline = VRaster::toFTOutline(newPath);
+            mRle = VRaster::instance().generateStrokeInfo(outline, mStroke.cap, mStroke.join,
+                                                          mStroke.width, mStroke.meterLimit);
+            VRaster::deleteFTOutline(outline);
+        } else {
+            FTOutline *outline = VRaster::toFTOutline(mPath);
+            mRle = VRaster::instance().generateFillInfo(outline, mFillRule);
+            VRaster::deleteFTOutline(outline);
+        }
+        mFlag &= ~DirtyFlag(DirtyState::Path);
+    }
+}
+
+void VDrawable::setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit, float strokeWidth)
+{
+    mStroke.enable = true;
+    mStroke.cap = cap;
+    mStroke.join = join;
+    mStroke.meterLimit = meterLimit;
+    mStroke.width = strokeWidth;
+    mFlag |= DirtyState::Path;
+}
+void VDrawable::setDashInfo(float *array, int size)
+{
+    mStroke.dashArray = array;
+    mStroke.dashArraySize = size;
+    mFlag |= DirtyState::Path;
+}
+
+void VDrawable::sync()
+{
+    mCNode.mFlag = ChangeFlagNone;
+    if (mFlag & DirtyState::None) return;
+
+    if (mFlag & DirtyState::Path) {
+        const std::vector<VPath::Element> &elm = mPath.elements();
+        const std::vector<VPointF> &pts  = mPath.points();
+        const float *ptPtr = reinterpret_cast<const float *>(pts.data());
+        const char *elmPtr = reinterpret_cast<const char *>(elm.data());
+        mCNode.mPath.elmPtr = elmPtr;
+        mCNode.mPath.elmCount = elm.size();
+        mCNode.mPath.ptPtr = ptPtr;
+        mCNode.mPath.ptCount = 2 * pts.size();
+        mCNode.mFlag |= ChangeFlagPath;
+    }
+
+    if (mStroke.enable) {
+        mCNode.mStroke.width = mStroke.width;
+        mCNode.mStroke.meterLimit = mStroke.meterLimit;
+        mCNode.mStroke.enable = 1;
+
+        switch (mFillRule) {
+        case FillRule::EvenOdd:
+            mCNode.mFillRule = LOTNode::EvenOdd;
+            break;
+        default:
+            mCNode.mFillRule = LOTNode::Winding;
+            break;
+        }
+
+        switch (mStroke.cap) {
+        case CapStyle::Flat:
+            mCNode.mStroke.cap = LOTNode::FlatCap;
+            break;
+        case CapStyle::Square:
+            mCNode.mStroke.cap = LOTNode::SquareCap;
+            break;
+        case CapStyle::Round:
+            mCNode.mStroke.cap = LOTNode::RoundCap;
+            break;
+        default:
+            mCNode.mStroke.cap = LOTNode::FlatCap;
+            break;
+        }
+
+        switch (mStroke.join) {
+        case JoinStyle::Miter:
+            mCNode.mStroke.join = LOTNode::MiterJoin;
+            break;
+        case JoinStyle::Bevel:
+            mCNode.mStroke.join = LOTNode::BevelJoin;
+            break;
+        case JoinStyle::Round:
+            mCNode.mStroke.join = LOTNode::RoundJoin;
+            break;
+        default:
+            mCNode.mStroke.join = LOTNode::MiterJoin;
+            break;
+        }
+
+        mCNode.mStroke.dashArray = mStroke.dashArray;
+        mCNode.mStroke.dashArraySize = mStroke.dashArraySize;
+
+    } else {
+        mCNode.mStroke.enable = 0;
+    }
+
+    switch (mBrush.type()) {
+    case VBrush::Type::Solid:
+        mCNode.mType = LOTNode::BrushSolid;
+        mCNode.mColor.r = mBrush.mColor.r;
+        mCNode.mColor.g = mBrush.mColor.g;
+        mCNode.mColor.b = mBrush.mColor.b;
+        mCNode.mColor.a = mBrush.mColor.a;
+        break;
+    case VBrush::Type::LinearGradient:
+        mCNode.mType = LOTNode::BrushGradient;
+        mCNode.mGradient.type = LOTNode::Gradient::Linear;
+        mCNode.mGradient.start.x = mBrush.mGradient->linear.x1;
+        mCNode.mGradient.start.y = mBrush.mGradient->linear.y1;
+        mCNode.mGradient.end.x = mBrush.mGradient->linear.x2;
+        mCNode.mGradient.end.y = mBrush.mGradient->linear.y2;
+        break;
+    case VBrush::Type::RadialGradient:
+        mCNode.mType = LOTNode::BrushGradient;
+        mCNode.mGradient.type = LOTNode::Gradient::Radial;
+        mCNode.mGradient.center.x = mBrush.mGradient->radial.cx;
+        mCNode.mGradient.center.y = mBrush.mGradient->radial.cy;
+        mCNode.mGradient.focal.x = mBrush.mGradient->radial.fx;
+        mCNode.mGradient.focal.y = mBrush.mGradient->radial.fy;
+        mCNode.mGradient.cradius = mBrush.mGradient->radial.cradius;
+        mCNode.mGradient.fradius = mBrush.mGradient->radial.fradius;
+        break;
+    default:
+        break;
+    }
+}
+
+/* Lottie Layer Rules
+ * 1. time stretch is pre calculated and applied to all the properties of the lottilayer model and all its children
+ * 2. The frame property could be reversed using,time-reverse layer property in AE. which means (start frame > endFrame)
+ * 3.
+ */
+
+LOTCompItem::LOTCompItem(LOTModel *model):mRootModel(model), mUpdateViewBox(false),mCurFrameNo(-1)
+{
+   // 1. build layer item list
+   mCompData = model->mRoot.get();
+   for(auto i : mCompData->mChildren) {
+      LOTLayerData *layerData = dynamic_cast<LOTLayerData *>(i.get());
+      if (layerData) {
+         LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerData);
+         if (layerItem) {
+            mLayers.push_back(layerItem);
+            mLayerMap[layerItem->id()] = layerItem;
+         }
+      }
+   }
+
+   //2. update parent layer
+   for(auto i : mLayers) {
+      int id = i->parentId();
+      if (id >=0) {
+         auto search = mLayerMap.find(id);
+         if (search != mLayerMap.end()) {
+           LOTLayerItem *parentLayer = search->second;
+           i->setParentLayer(parentLayer);
+         }
+      }
+   }
+   //3. update static property of each layer
+   for(auto i : mLayers) {
+      i->updateStaticProperty();
+   }
+
+   mViewSize = mCompData->size();
+}
+
+LOTCompItem::~LOTCompItem()
+{
+    for(auto i : mLayers) {
+       delete i;
+    }
+}
+
+LOTLayerItem *
+LOTCompItem::createLayerItem(LOTLayerData *layerData)
+{
+    switch(layerData->mLayerType) {
+        case LayerType::Precomp: {
+            return new LOTCompLayerItem(layerData);
+            break;
+        }
+        case LayerType::Solid: {
+            return new LOTSolidLayerItem(layerData);
+            break;
+        }
+        case LayerType::Shape: {
+            return new LOTShapeLayerItem(layerData);
+            break;
+        }
+        case LayerType::Null: {
+            return new LOTNullLayerItem(layerData);
+            break;
+        }
+        default:
+            return nullptr;
+            break;
+    }
+}
+
+void LOTCompItem::resize(const VSize &size)
+{
+   if (mViewSize == size) return;
+   mViewSize = size;
+   mUpdateViewBox = true;
+}
+
+VSize LOTCompItem::size() const
+{
+   return mViewSize;
+}
+
+bool LOTCompItem::update(int frameNo)
+{
+   VMatrix m;
+   float sx, sy;
+
+   // check if cached frame is same as requested frame.
+   if (!mUpdateViewBox && (mCurFrameNo == frameNo)) return false;
+
+   sx = mViewSize.width() / float(mCompData->size().width());
+   sy = mViewSize.height() / float(mCompData->size().height());
+   float scale = fmin(sx, sy);
+   m.scale(scale, scale);
+
+   // update the layer from back to front
+   for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
+      LOTLayerItem *layer = *i;
+      layer->update(frameNo, m, 1.0);
+   }
+   buildRenderList();
+   mCurFrameNo = frameNo;
+   mUpdateViewBox = false;
+   return true;
+}
+
+void LOTCompItem::buildRenderList()
+{
+    mRenderList.clear();
+    std::vector<VDrawable *> list;
+    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
+       LOTLayerItem *layer = *i;
+       layer->renderList(list);
+    }
+
+    for(auto i : list) {
+        i->sync();
+        mRenderList.push_back(&i->mCNode);
+    }
+}
+
+const std::vector<LOTNode *>& LOTCompItem::renderList() const
+{
+    return mRenderList;
+}
+
+bool LOTCompItem::render(const LOTBuffer &buffer)
+{
+    VBitmap bitmap((uchar *)buffer.buffer, buffer.width, buffer.height,
+                   buffer.bytesPerLine, VBitmap::Format::ARGB32_Premultiplied, nullptr, nullptr);
+
+    VPainter painter(&bitmap);
+    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
+       LOTLayerItem *layer = *i;
+       layer->render(&painter);
+    }
+
+//    for(auto i : mRenderList) {
+//        if (i->mType == LOTNode::TypeFill) {
+//            if (i->mFlag & ChangeFlagPath) {
+//                FTOutline *outline = VRaster::toFTOutline(i->mFinalPath);
+//                i->mRleInfo = VRaster::instance().generateFillInfo(outline);
+//                VRaster::deleteFTOutline(outline);
+//                i->mFlag = ChangeFlagNone;
+//            }
+//            VBrush brush(i->mColor.r, i->mColor.g, i->mColor.b, i->mColor.a);
+//            painter.setBrush(brush);
+//            if (!i->mMaskRle.isEmpty()) {
+//                VRle rle = i->mRleInfo + i->mMaskRle;
+//                painter.drawRle(VPoint(), rle);
+//            } else {
+//                painter.drawRle(VPoint(), i->mRleInfo);
+//            }
+//        } else if(i->mType == LOTNode::TypeStroke) {
+//            if (i->mFlag & ChangeFlagPath) {
+//                VPath final = i->mFinalPath;
+//                if (i->mStroke.dashArraySize) {
+//                    VDasher dasher(i->mStroke.dashArray, i->mStroke.dashArraySize);
+//                    final = dasher.dashed(i->mFinalPath);
+//                }
+//                FTOutline *outline = VRaster::toFTOutline(final);
+//                i->mRleInfo = VRaster::instance().generateStrokeInfo(outline, CapStyle::Round, JoinStyle::Round, i->mStroke.width);
+//                VRaster::deleteFTOutline(outline);
+//                i->mFlag = ChangeFlagNone;
+//            }
+//            VBrush brush(i->mColor.r, i->mColor.g, i->mColor.b, i->mColor.a);
+//            painter.setBrush(brush);
+//            painter.drawRle(VPoint(), i->mRleInfo);
+//        } else if (i->mType == LOTNode::Type::TypeGFill) {
+//            if (i->mFlag & ChangeFlagPath) {
+//                FTOutline *outline = VRaster::toFTOutline(i->mFinalPath);
+//                i->mRleInfo = VRaster::instance().generateFillInfo(outline);
+//                VRaster::deleteFTOutline(outline);
+//                i->mFlag = ChangeFlagNone;
+//            }
+//            painter.setBrush(i->mBrush);
+//            painter.drawRle(VPoint(), i->mRleInfo);
+
+//        }
+//    }
+    return true;
+}
+
+void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
+                         float parentAlpha, const DirtyFlag &flag)
+{
+    if (mData->mShape.isStatic()) {
+        if (mLocalPath.isEmpty()) {
+            mLocalPath = mData->mShape.value(frameNo).toPath();
+        }
+    } else {
+        mLocalPath = mData->mShape.value(frameNo).toPath();
+    }
+    float opacity = mData->opacity(frameNo);
+    opacity = opacity * parentAlpha;
+
+    VPath path = mLocalPath;
+    path.transform(parentMatrix);
+
+    FTOutline *outline = VRaster::toFTOutline(path);
+    mRle = VRaster::instance().generateFillInfo(outline);
+    VRaster::deleteFTOutline(outline);
+
+    mRle = mRle * (opacity * 255);
+
+    if (mData->mInv) {
+        mRle = ~mRle;
+    }
+}
+
+void LOTLayerItem::render(VPainter *painter)
+{
+    std::vector<VDrawable *> list;
+    renderList(list);
+    VRle mask = maskRle();
+    for(auto i : list) {
+        i->preprocess();
+        painter->setBrush(i->mBrush);
+        if (!mask.isEmpty()) {
+            VRle rle = i->mRle + mask;
+            painter->drawRle(VPoint(), rle);
+        } else {
+            painter->drawRle(VPoint(), i->mRle);
+        }
+    }
+}
+
+VRle LOTLayerItem::maskRle()
+{
+    VRle rle;
+    for (auto &i : mMasks) {
+        switch (i->maskMode()) {
+            case LOTMaskData::Mode::Add: {
+                rle = rle + i->mRle;
+                break;
+            }
+            case LOTMaskData::Mode::Substarct: {
+                rle = rle - i->mRle;
+                break;
+            }
+            case LOTMaskData::Mode::Intersect: {
+                rle = rle & i->mRle;
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return rle;
+}
+
+LOTLayerItem::LOTLayerItem(LOTLayerData *layerData):mLayerData(layerData),
+                                                    mParentLayer(nullptr),
+                                                    mFrameNo(-1),
+                                                    mDirtyFlag(DirtyFlagBit::All)
+{
+    if (mLayerData->mHasMask) {
+        for (auto i : mLayerData->mMasks) {
+            mMasks.push_back(std::unique_ptr<LOTMaskItem>(new LOTMaskItem(i.get())));
+        }
+    }
+}
+
+void LOTLayerItem::updateStaticProperty()
+{
+   if (mParentLayer)
+     mParentLayer->updateStaticProperty();
+
+   mStatic = mParentLayer ? (mLayerData->isStatic() & mParentLayer->isStatic()) : mLayerData->isStatic();
+}
+
+void LOTLayerItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha)
+{
+   mFrameNo = frameNo;
+   // 1. check if the layer is part of the current frame
+   if (!visible()) return;
+
+   // 2. calculate the parent matrix and alpha
+   VMatrix m = matrix(frameNo) * parentMatrix;
+   float alpha = parentAlpha * opacity(frameNo);
+
+   //6. update the mask
+   if (hasMask()) {
+       for (auto &i : mMasks)
+           i->update(frameNo, m, alpha, mDirtyFlag);
+   }
+
+   // 3. update the dirty flag based on the change
+   if (flag() & DirtyFlagBit::All) {
+      mCombinedMatrix = m;
+      mCombinedAlpha = alpha;
+   } else {
+      if (!mCombinedMatrix.fuzzyCompare(m)) {
+         mDirtyFlag |= DirtyFlagBit::Matrix;
+         mCombinedMatrix = m;
+      }
+      if (!vCompare(mCombinedAlpha, alpha)) {
+         mDirtyFlag |= DirtyFlagBit::Alpha;
+         mCombinedAlpha = alpha;
+      }
+   }
+
+   // 4. if no parent property change and layer is static then nothing to do.
+   //@TODO fix this regression
+//   if ((flag() & DirtyFlagBit::None) && isStatic())
+//      return;
+
+   //5. update the content of the layer
+   updateContent();
+
+   //6. reset the dirty flag
+   mDirtyFlag = DirtyFlagBit::None;
+}
+
+float
+LOTLayerItem::opacity(int frameNo) const
+{
+   return mLayerData->mTransform->opacity(frameNo);
+}
+
+VMatrix
+LOTLayerItem::matrix(int frameNo) const
+{
+    if (mParentLayer)
+        return mLayerData->mTransform->matrix(frameNo) * mParentLayer->matrix(frameNo);
+    else
+        return mLayerData->mTransform->matrix(frameNo);
+}
+
+bool LOTLayerItem::visible() const
+{
+   if (frameNo() >= mLayerData->inFrame() && frameNo() < mLayerData->outFrame())
+      return true;
+   else
+      return false;
+}
+
+
+
+LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel):LOTLayerItem(layerModel)
+{
+   for(auto i : mLayerData->mChildren) {
+      LOTLayerData *layerModel = dynamic_cast<LOTLayerData *>(i.get());
+      if (layerModel) {
+         LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerModel);
+         if (layerItem) {
+            mLayers.push_back(layerItem);
+            mLayerMap[layerItem->id()] = layerItem;
+         }
+      }
+   }
+
+   //2. update parent layer
+   for(auto i : mLayers) {
+      int id = i->parentId();
+      if (id >=0) {
+         auto search = mLayerMap.find(id);
+         if (search != mLayerMap.end()) {
+           LOTLayerItem *parentLayer = search->second;
+           i->setParentLayer(parentLayer);
+         }
+      }
+   }
+   for(auto i : mLayers) {
+       i->updateStaticProperty();
+   }
+}
+
+LOTCompLayerItem::~LOTCompLayerItem()
+{
+    for(auto i : mLayers) {
+       delete i;
+    }
+}
+
+void LOTCompLayerItem::updateContent()
+{
+    // update the layer from back to front
+    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
+       LOTLayerItem *layer = *i;
+       layer->update(frameNo(), combinedMatrix(), combinedAlpha());
+    }
+}
+
+void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
+{
+    if (!visible()) return;
+
+    // update the layer from back to front
+    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
+       LOTLayerItem *layer = *i;
+       layer->renderList(list);
+    }
+}
+
+LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
+{
+
+}
+
+void LOTSolidLayerItem::updateContent()
+{
+   if (!mRenderNode) {
+      mRenderNode = std::unique_ptr<VDrawable>(new VDrawable());
+      mRenderNode->mType = VDrawable::Type::Fill;
+      mRenderNode->mFlag |= VDrawable::DirtyState::All;
+   }
+
+   if (flag() & DirtyFlagBit::Matrix) {
+       VPath path;
+       path.addRect(VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
+       path.transform(combinedMatrix());
+       mRenderNode->mFlag |= VDrawable::DirtyState::Path;
+       mRenderNode->mPath = path;
+   }
+   if (flag() & DirtyFlagBit::Alpha) {
+       LottieColor color = mLayerData->solidColor();
+       VBrush brush(color.toColor(combinedAlpha()));
+       mRenderNode->setBrush(brush);
+       mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
+   }
+}
+
+void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
+{
+    if (!visible()) return;
+
+    list.push_back(mRenderNode.get());
+}
+
+LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
+{
+
+}
+void LOTNullLayerItem::updateContent()
+{
+
+}
+
+
+LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
+{
+    mRoot = new LOTContentGroupItem(nullptr);
+    mRoot->addChildren(layerData);
+    mRoot->processPaintOperation();
+}
+
+LOTShapeLayerItem::~LOTShapeLayerItem()
+{
+    delete mRoot;
+}
+
+LOTContentItem * LOTShapeLayerItem::createContentItem(LOTData *contentData)
+{
+    switch(contentData->type()) {
+        case LOTData::Type::ShapeGroup: {
+            return new LOTContentGroupItem(static_cast<LOTShapeGroupData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Rect: {
+            return new LOTRectItem(static_cast<LOTRectData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Ellipse: {
+            return new LOTEllipseItem(static_cast<LOTEllipseData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Shape: {
+            return new LOTShapeItem(static_cast<LOTShapeData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Polystar: {
+            return new LOTPolystarItem(static_cast<LOTPolystarData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Fill: {
+            return new LOTFillItem(static_cast<LOTFillData *>(contentData));
+            break;
+        }
+        case LOTData::Type::GFill: {
+            return new LOTGFillItem(static_cast<LOTGFillData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Stroke: {
+            return new LOTStrokeItem(static_cast<LOTStrokeData *>(contentData));
+            break;
+        }
+        case LOTData::Type::GStroke: {
+            return new LOTGStrokeItem(static_cast<LOTGStrokeData *>(contentData));
+            break;
+        }
+        case LOTData::Type::Repeater: {
+                return new LOTRepeaterItem(static_cast<LOTRepeaterData *>(contentData));
+                break;
+            }
+        default:
+            return nullptr;
+            break;
+    }
+}
+
+void LOTShapeLayerItem::updateContent()
+{
+   mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
+}
+
+void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
+{
+    if (!visible()) return;
+    mRoot->renderList(list);
+}
+
+VPath::Direction LOTContentItem::direction(bool isCW)
+{
+   if (isCW)
+      return VPath::Direction::CW;
+   else
+      return VPath::Direction::CCW;
+}
+
+
+LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data):mData(data)
+{
+   addChildren(mData);
+}
+
+void LOTContentGroupItem::addChildren(LOTGroupData *data)
+{
+   if (!data) return;
+
+   for(auto i : data->mChildren) {
+      LOTData *data = i.get();
+      LOTContentItem *content = LOTShapeLayerItem::createContentItem(data);
+      if (content)
+         mContents.push_back(content);
+   }
+}
+
+LOTContentGroupItem::~LOTContentGroupItem()
+{
+    for(auto i : mContents) {
+        delete i;
+    }
+}
+
+
+void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+{
+   VMatrix m = parentMatrix;
+   float alpha = parentAlpha;
+   DirtyFlag newFlag = flag;
+
+   if (mData) {
+      // update the matrix and the flag
+      if ((flag & DirtyFlagBit::Matrix) || !mData->mTransform->staticMatrix() ) {
+         newFlag |= DirtyFlagBit::Matrix;
+      }
+      m = mData->mTransform->matrix(frameNo) * parentMatrix;
+      alpha *= mData->mTransform->opacity(frameNo);
+
+      if (!floatCmp(alpha, parentAlpha)) {
+         newFlag |= DirtyFlagBit::Alpha;
+      }
+   }
+
+   for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
+      (*i)->update(frameNo, m, alpha, newFlag);
+   }
+}
+
+void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
+{
+    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
+       (*i)->renderList(list);
+    }
+}
+
+void LOTContentGroupItem::processPaintOperation()
+{
+   std::vector<LOTPaintDataItem *> list;
+   paintOperationHelper(list);
+}
+
+void LOTContentGroupItem::paintOperationHelper(std::vector<LOTPaintDataItem *> &list)
+{
+   int curOpCount = list.size();
+   for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
+      auto child = *i;
+      if (auto pathNode = dynamic_cast<LOTPathDataItem *>(child)) {
+         // the node is a path data node add the paint operation list to it.
+         pathNode->addPaintOperation(list, curOpCount);
+      } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(child)) {
+         // add it to the paint operation list
+         list.push_back(paintNode);
+      } else if (auto groupNode = dynamic_cast<LOTContentGroupItem *>(child)) {
+         // update the groups node with current list
+         groupNode->paintOperationHelper(list);
+      }
+   }
+   list.erase(list.begin() + curOpCount, list.end());
+}
+
+void LOTPathDataItem::addPaintOperation(std::vector<LOTPaintDataItem *> &list, int externalCount)
+{
+    for(auto paintItem : list) {
+      bool sameGroup = (externalCount-- > 0) ? false : true;
+      mNodeList.push_back(std::unique_ptr<VDrawable>(new VDrawable()));
+      mRenderList.push_back(LOTRenderNode(this, paintItem, mNodeList.back().get(), sameGroup));
+    }
+}
+
+
+void LOTPathDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+{
+   mPathChanged = false;
+   mCombinedAlpha = parentAlpha;
+
+   // 1. update the local path if needed
+   if (!(mInit && mStaticPath)) {
+      mLocalPath = getPath(frameNo);
+      mInit = true;
+      mPathChanged = true;
+   }
+
+   // 2. apply path operation if needed
+   // TODO
+
+   // 3. compute the final path with parentMatrix
+   if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
+      mFinalPath = mLocalPath;
+      mFinalPath.transform(parentMatrix);
+      mPathChanged = true;
+   }
+
+   // 2. update the rendernode list
+   for (const auto &i : mRenderList) {
+      i.drawable->mFlag = VDrawable::DirtyState::None;
+      i.paintNodeRef->updateRenderNode(i.pathNodeRef, i.drawable, i.sameGroup);
+      if (mPathChanged) {
+          i.drawable->mPath = mFinalPath;
+          i.drawable->mFlag |= VDrawable::DirtyState::Path;
+      }
+   }
+}
+
+void LOTPathDataItem::renderList(std::vector<VDrawable *> &list)
+{
+   for (const auto &i : mRenderList) {
+       list.push_back(i.drawable);
+   }
+}
+
+VPath LOTPathDataItem::path() const
+{
+   return mFinalPath;
+}
+
+
+LOTRectItem::LOTRectItem(LOTRectData *data):LOTPathDataItem(data->isStatic()),mData(data)
+{
+}
+
+VPath LOTRectItem::getPath(int frameNo)
+{
+   VPointF pos = mData->mPos.value(frameNo);
+   VPointF size = mData->mSize.value(frameNo);
+   float radius = mData->mRound.value(frameNo);
+   VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
+
+   VPath path;
+   path.addRoundRect(r, radius, radius, direction(mData->isDirectionCW()));
+
+   return path;
+}
+
+LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data):LOTPathDataItem(data->isStatic()),mData(data)
+{
+
+}
+
+VPath LOTEllipseItem::getPath(int frameNo)
+{
+   VPointF pos = mData->mPos.value(frameNo);
+   VPointF size = mData->mSize.value(frameNo);
+   VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
+
+   VPath path;
+   path.addOval(r, direction(mData->isDirectionCW()));
+
+   return path;
+}
+
+LOTShapeItem::LOTShapeItem(LOTShapeData *data):LOTPathDataItem(data->isStatic()),mData(data)
+{
+
+}
+
+VPath LOTShapeItem::getPath(int frameNo)
+{
+    LottieShapeData shapeData = mData->mShape.value(frameNo);
+
+    if (shapeData.mPoints.empty())
+     return VPath();
+
+    VPath path;
+
+    int size = shapeData.mPoints.size();
+    const VPointF *points = shapeData.mPoints.data();
+    path.moveTo(points[0]);
+    for (int i = 1 ; i < size; i+=3) {
+       path.cubicTo(points[i], points[i+1], points[i+2]);
+    }
+    if (shapeData.mClosed)
+      path.close();
+
+   return path;
+}
+
+
+LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data):LOTPathDataItem(data->isStatic()),mData(data)
+{
+
+}
+
+VPath LOTPolystarItem::getPath(int frameNo)
+{
+   VPointF pos = mData->mPos.value(frameNo);
+   float points = mData->mPointCount.value(frameNo);
+   float innerRadius = mData->mInnerRadius.value(frameNo);
+   float outerRadius = mData->mOuterRadius.value(frameNo);
+   float innerRoundness = mData->mInnerRoundness.value(frameNo);
+   float outerRoundness = mData->mOuterRoundness.value(frameNo);
+   float rotation = mData->mRotation.value(frameNo);
+
+   VPath path;
+   VMatrix m;
+
+   if (mData->mType == LOTPolystarData::PolyType::Star) {
+        path.addPolystarStar(0.0, 0.0, 0.0, points,
+                             innerRadius, outerRadius,
+                             innerRoundness, outerRoundness,
+                             direction(mData->isDirectionCW()));
+   } else {
+        path.addPolystarPolygon(0.0, 0.0, 0.0, points,
+                                outerRadius, outerRoundness,
+                                direction(mData->isDirectionCW()));
+   }
+
+   m.translate(pos.x(), pos.y()).rotate(rotation);
+   m.rotate(rotation);
+   path.transform(m);
+
+   return path;
+}
+
+
+
+/*
+ * PaintData Node handling
+ *
+ */
+
+void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+{
+   mContentChanged = false;
+   mParentAlpha = parentAlpha;
+   mParentMatrix = parentMatrix;
+   mFlag = flag;
+   mFrameNo = frameNo;
+   // 1. update the local content if needed
+  // if (!(mInit && mStaticContent)) {
+      mInit = true;
+      updateContent(frameNo);
+      mContentChanged = true;
+  // }
+}
+
+
+LOTFillItem::LOTFillItem(LOTFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+{
+}
+
+void LOTFillItem::updateContent(int frameNo)
+{
+   LottieColor c = mData->mColor.value(frameNo);
+   float opacity = mData->opacity(frameNo);
+   mColor = c.toColor(opacity);
+   mFillRule = mData->fillRule();
+}
+
+void LOTFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+{
+    VColor color = mColor;
+    if (sameParent)
+      color.setAlpha(color.a * pathNode->combinedAlpha());
+    else
+      color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
+    VBrush brush(color);
+    drawable->setBrush(brush);
+    drawable->setFillRule(mFillRule);
+}
+
+
+LOTGFillItem::LOTGFillItem(LOTGFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+{
+}
+
+void LOTGFillItem::updateContent(int frameNo)
+{
+    mData->update(mGradient, frameNo);
+    mGradient->mMatrix = mParentMatrix;
+    mFillRule = mData->fillRule();
+}
+
+void LOTGFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+{
+    drawable->setBrush(VBrush(mGradient.get()));
+    drawable->setFillRule(mFillRule);
+}
+
+LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+{
+    mDashArraySize = 0;
+}
+
+void LOTStrokeItem::updateContent(int frameNo)
+{
+    LottieColor c = mData->mColor.value(frameNo);
+    float opacity = mData->opacity(frameNo);
+    mColor = c.toColor(opacity);
+    mCap = mData->capStyle();
+    mJoin = mData->joinStyle();
+    mMiterLimit = mData->meterLimit();
+    mWidth = mData->width(frameNo);
+    if (mData->hasDashInfo()) {
+        mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
+    }
+}
+
+static float getScale(const VMatrix &matrix)
+{
+    constexpr float SQRT_2 = 1.41421;
+    VPointF p1(0,0);
+    VPointF p2(SQRT_2,SQRT_2);
+    p1 = matrix.map(p1);
+    p2 = matrix.map(p2);
+    VPointF final = p2 - p1;
+
+    return std::sqrt( final.x() * final.x() + final.y() * final.y());
+}
+
+void LOTStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+{
+    VColor color = mColor;
+    if (sameParent)
+      color.setAlpha(color.a * pathNode->combinedAlpha());
+    else
+      color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
+
+    VBrush brush(color);
+    drawable->setBrush(brush);
+
+    drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
+    if (mDashArraySize) {
+        drawable->setDashInfo(mDashArray, mDashArraySize);
+    }
+}
+
+LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+{
+    mDashArraySize = 0;
+}
+
+void LOTGStrokeItem::updateContent(int frameNo)
+{
+    mData->update(mGradient, frameNo);
+    mGradient->mMatrix = mParentMatrix;
+    mCap = mData->capStyle();
+    mJoin = mData->joinStyle();
+    mMiterLimit = mData->meterLimit();
+    mWidth = mData->width(frameNo);
+    if (mData->hasDashInfo()) {
+        mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
+    }
+}
+
+void LOTGStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+{
+    drawable->setBrush(VBrush(mGradient.get()));
+    drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
+    if (mDashArraySize) {
+        drawable->setDashInfo(mDashArray, mDashArraySize);
+    }
+}
+
+LOTTrimItem::LOTTrimItem(LOTTrimData *data):mData(data)
+{
+
+}
+
+LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data):mData(data)
+{
+
+}
+
+void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+{
+
+}
+
+void LOTRepeaterItem::renderList(std::vector<VDrawable *> &list)
+{
+
+}
+
diff --git a/src/lottie/lottieitem.h b/src/lottie/lottieitem.h
new file mode 100644 (file)
index 0000000..829ac07
--- /dev/null
@@ -0,0 +1,396 @@
+#ifndef LOTTIEITEM_H
+#define LOTTIEITEM_H
+
+#include<lottiemodel.h>
+#include<sstream>
+#include<memory>
+
+#include"vmatrix.h"
+#include"vpath.h"
+#include"vpoint.h"
+#include"lottieplayer.h"
+#include"vbrush.h"
+#include"vpainter.h"
+
+enum class DirtyFlagBit
+{
+   None   = 0x0001,
+   Matrix = 0x0010,
+   Alpha  = 0x0100,
+   All    = (Matrix | Alpha)
+};
+
+class LOTLayerItem;
+class LOTMaskItem;
+class VDrawable;
+
+class LOTCompItem
+{
+public:
+   LOTCompItem(LOTModel *model);
+   ~LOTCompItem();
+   static LOTLayerItem * createLayerItem(LOTLayerData *layerData);
+   bool update(int frameNo);
+   void resize(const VSize &size);
+   VSize size() const;
+   const std::vector<LOTNode *>& renderList()const;
+   void buildRenderList();
+   bool render(const LOTBuffer &buffer);
+private:
+   VMatrix                                    mScaleMatrix;
+   VSize                                      mViewSize;
+   LOTModel                                   *mRootModel;
+   LOTCompositionData                         *mCompData;
+   std::vector<LOTLayerItem *>                 mLayers;
+   std::unordered_map<int, LOTLayerItem *>     mLayerMap;
+   bool                                        mUpdateViewBox;
+   int                                         mCurFrameNo;
+   std::vector<LOTNode *>                      mRenderList;
+};
+
+typedef vFlag<DirtyFlagBit> DirtyFlag;
+class LOTLayerItem
+{
+public:
+   LOTLayerItem(LOTLayerData *layerData);
+   virtual ~LOTLayerItem(){}
+   int id() const {return mLayerData->id();}
+   int parentId() const {return mLayerData->parentId();}
+   void setParentLayer(LOTLayerItem *parent){mParentLayer = parent;}
+   virtual void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha);
+   VMatrix matrix(int frameNo) const;
+   virtual void renderList(std::vector<VDrawable *> &list){}
+   void updateStaticProperty();
+   void render(VPainter *painter);
+protected:
+   virtual void updateContent() = 0;
+   inline VMatrix combinedMatrix() const {return mCombinedMatrix;}
+   inline int frameNo() const {return mFrameNo;}
+   inline float combinedAlpha() const {return mCombinedAlpha;}
+   inline bool isStatic() const {return mStatic;}
+   float opacity(int frameNo) const;
+   bool visible() const;
+   inline DirtyFlag flag() const {return mDirtyFlag;}
+   VRle maskRle();
+   bool hasMask() const {return !mMasks.empty();}
+protected:
+   std::vector<std::unique_ptr<LOTMaskItem>>   mMasks;
+   LOTLayerData                               *mLayerData;
+   LOTLayerItem                               *mParentLayer;
+   VMatrix                                    mCombinedMatrix;
+   float                                       mCombinedAlpha;
+   int                                         mFrameNo;
+   DirtyFlag                                   mDirtyFlag;
+   bool                                        mVisible;
+   bool                                        mStatic;
+};
+
+class LOTCompLayerItem: public LOTLayerItem
+{
+public:
+   ~LOTCompLayerItem();
+   LOTCompLayerItem(LOTLayerData *layerData);
+   void renderList(std::vector<VDrawable *> &list)final;
+protected:
+   void updateContent() final;
+private:
+   std::vector<LOTLayerItem *>                  mLayers;
+   std::unordered_map<int, LOTLayerItem *>      mLayerMap;
+   int                                          mLastFrame;
+};
+
+class LOTSolidLayerItem: public LOTLayerItem
+{
+public:
+   LOTSolidLayerItem(LOTLayerData *layerData);
+protected:
+   void updateContent() final;
+   void renderList(std::vector<VDrawable *> &list) final;
+private:
+   std::unique_ptr<VDrawable>   mRenderNode;
+};
+
+class LOTContentItem;
+class LOTContentGroupItem;
+class LOTShapeLayerItem: public LOTLayerItem
+{
+public:
+   ~LOTShapeLayerItem();
+   LOTShapeLayerItem(LOTLayerData *layerData);
+   static LOTContentItem * createContentItem(LOTData *contentData);
+   void renderList(std::vector<VDrawable *> &list)final;
+protected:
+   void updateContent() final;
+   LOTContentGroupItem       *mRoot;
+};
+
+class LOTNullLayerItem: public LOTLayerItem
+{
+public:
+   LOTNullLayerItem(LOTLayerData *layerData);
+protected:
+   void updateContent() final;
+};
+
+class LOTMaskItem
+{
+public:
+    LOTMaskItem(LOTMaskData *data): mData(data), mCombinedAlpha(0){}
+    void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag);
+    LOTMaskData::Mode maskMode() const { return mData->mMode;}
+public:
+    LOTMaskData *mData;
+    float        mCombinedAlpha;
+    VMatrix     mCombinedMatrix;
+    VPath       mLocalPath;
+    VRle        mRle;
+};
+
+class VDrawable
+{
+public:
+    enum class DirtyState {
+          None   = 0x00000000,
+          Path   = 0x00000001,
+          Stroke = 0x00000010,
+          Brush  = 0x00000100,
+          All    = (None | Path | Stroke | Brush)
+    };
+    enum class Type {
+        Fill,
+        Stroke,
+    };
+    typedef vFlag<DirtyState> DirtyFlag;
+    VDrawable();
+    void sync();
+    void setPath(const VPath &path);
+    void setFillRule(FillRule rule){mFillRule = rule;}
+    void setBrush(const VBrush &brush){mBrush = brush;}
+    void setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit, float strokeWidth);
+    void setDashInfo(float *array, int size);
+    void preprocess();
+public:
+    DirtyFlag          mFlag;
+    VDrawable::Type    mType;
+    VBrush             mBrush;
+    VPath             mPath;
+    FillRule           mFillRule;
+    VRle              mRle;
+    struct  {
+         bool         enable;
+         float        width;
+         CapStyle     cap;
+         JoinStyle    join;
+         float        meterLimit;
+         float       *dashArray;
+         int          dashArraySize;
+    }mStroke;
+    LOTNode           mCNode;
+};
+
+class LOTNode;
+class LOTPathDataItem;
+class LOTPaintDataItem;
+struct LOTRenderNode
+{
+   LOTRenderNode(LOTPathDataItem *path, LOTPaintDataItem *paint, VDrawable *render, bool sameG)
+                  :pathNodeRef(path), paintNodeRef(paint), drawable(render), sameGroup(sameG){}
+   LOTPathDataItem  *pathNodeRef;
+   LOTPaintDataItem *paintNodeRef;
+   VDrawable        *drawable;
+   bool              sameGroup;
+};
+
+class LOTContentItem
+{
+public:
+   LOTContentItem(){}
+   virtual ~LOTContentItem(){}
+   virtual void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) = 0;
+   virtual void renderList(std::vector<VDrawable *> &list){}
+   VPath::Direction direction(bool isCW);
+};
+
+class LOTContentGroupItem: public LOTContentItem
+{
+public:
+   ~LOTContentGroupItem();
+   LOTContentGroupItem(LOTShapeGroupData *data);
+   void addChildren(LOTGroupData *data);
+   void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) final;
+   void processPaintOperation();
+   void renderList(std::vector<VDrawable *> &list) final;
+private:
+   void paintOperationHelper(std::vector<LOTPaintDataItem *> &list);
+   LOTShapeGroupData                 *mData;
+   std::vector<LOTContentItem *>      mContents;
+};
+
+class LOTPathDataItem : public LOTContentItem
+{
+public:
+   LOTPathDataItem(bool staticPath):mInit(false), mStaticPath(staticPath){}
+   void addPaintOperation(std::vector<LOTPaintDataItem *> &list, int externalCount);
+   void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) final;
+   VPath path() const;
+   inline float combinedAlpha() const{ return mCombinedAlpha;}
+   void renderList(std::vector<VDrawable *> &list) final;
+private:
+   std::vector<LOTRenderNode>              mRenderList;
+   std::vector<std::unique_ptr<VDrawable>> mNodeList;
+   bool                                    mInit;
+   bool                                    mStaticPath;
+   VPath                                  mLocalPath;
+   VPath                                  mFinalPath;
+   bool                                    mPathChanged;
+   float                                   mCombinedAlpha;
+protected:
+   virtual VPath getPath(int frameNo) = 0;
+};
+
+class LOTRectItem: public LOTPathDataItem
+{
+public:
+   LOTRectItem(LOTRectData *data);
+protected:
+   VPath getPath(int frameNo) final;
+   LOTRectData           *mData;
+};
+
+class LOTEllipseItem: public LOTPathDataItem
+{
+public:
+   LOTEllipseItem(LOTEllipseData *data);
+private:
+   VPath getPath(int frameNo) final;
+   LOTEllipseData           *mData;
+};
+
+class LOTShapeItem: public LOTPathDataItem
+{
+public:
+   LOTShapeItem(LOTShapeData *data);
+private:
+   VPath getPath(int frameNo) final;
+   LOTShapeData             *mData;
+};
+
+class LOTPolystarItem: public LOTPathDataItem
+{
+public:
+   LOTPolystarItem(LOTPolystarData *data);
+private:
+   VPath getPath(int frameNo) final;
+   LOTPolystarData             *mData;
+};
+
+
+
+class LOTPaintDataItem : public LOTContentItem
+{
+public:
+   LOTPaintDataItem(bool staticContent):mInit(false), mStaticContent(staticContent){}
+   virtual void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag);
+   virtual void updateRenderNode(LOTPathDataItem *pathNode, VDrawable *renderer, bool sameParent) = 0;
+protected:
+   virtual void updateContent(int frameNo) = 0;
+   inline float parentAlpha() const {return mParentAlpha;}
+   inline bool  contentChanged() const{return mContentChanged;}
+public:
+   float         mParentAlpha;
+   VMatrix      mParentMatrix;
+   DirtyFlag     mFlag;
+   int           mFrameNo;
+   bool          mInit;
+   bool          mStaticContent;
+   bool          mContentChanged;
+};
+
+class LOTFillItem : public LOTPaintDataItem
+{
+public:
+   LOTFillItem(LOTFillData *data);
+protected:
+   void updateContent(int frameNo) final;
+   void updateRenderNode(LOTPathDataItem *pathNode, VDrawable *renderer, bool sameParent) final;
+private:
+   LOTFillData             *mData;
+   VColor                  mColor;
+   FillRule                mFillRule;
+};
+
+class LOTGFillItem : public LOTPaintDataItem
+{
+public:
+   LOTGFillItem(LOTGFillData *data);
+protected:
+   void updateContent(int frameNo) final;
+   void updateRenderNode(LOTPathDataItem *pathNode, VDrawable *renderer, bool sameParent) final;
+private:
+   LOTGFillData                 *mData;
+   std::unique_ptr<VGradient>    mGradient;
+   FillRule                      mFillRule;
+};
+
+class LOTStrokeItem : public LOTPaintDataItem
+{
+public:
+   LOTStrokeItem(LOTStrokeData *data);
+protected:
+   void updateContent(int frameNo) final;
+   void updateRenderNode(LOTPathDataItem *pathNode, VDrawable *renderer, bool sameParent) final;
+private:
+   LOTStrokeData             *mData;
+   CapStyle                  mCap;
+   JoinStyle                 mJoin;
+   float                     mMiterLimit;
+   VColor                    mColor;
+   float                     mWidth;
+   float                     mDashArray[6];
+   int                       mDashArraySize;
+};
+
+class LOTGStrokeItem : public LOTPaintDataItem
+{
+public:
+   LOTGStrokeItem(LOTGStrokeData *data);
+protected:
+   void updateContent(int frameNo) final;
+   void updateRenderNode(LOTPathDataItem *pathNode, VDrawable *renderer, bool sameParent) final;
+private:
+   LOTGStrokeData               *mData;
+   std::unique_ptr<VGradient>    mGradient;
+   CapStyle                      mCap;
+   JoinStyle                     mJoin;
+   float                         mMiterLimit;
+   VColor                        mColor;
+   float                         mWidth;
+   float                         mDashArray[6];
+   int                           mDashArraySize;
+};
+
+
+// Trim Item
+
+class LOTTrimItem : public LOTContentItem
+{
+public:
+   LOTTrimItem(LOTTrimData *data);
+private:
+   LOTTrimData             *mData;
+};
+
+class LOTRepeaterItem : public LOTContentItem
+{
+public:
+   LOTRepeaterItem(LOTRepeaterData *data);
+   virtual void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) final;
+   virtual void renderList(std::vector<VDrawable *> &list) final;
+private:
+   LOTRepeaterData             *mData;
+};
+
+
+#endif // LOTTIEITEM_H
+
+
diff --git a/src/lottie/lottieloader.cpp b/src/lottie/lottieloader.cpp
new file mode 100644 (file)
index 0000000..e918490
--- /dev/null
@@ -0,0 +1,85 @@
+#include "lottieloader.h"
+#include "lottieparser.h"
+
+#include<fstream>
+#include<unordered_map>
+
+class LottieFileCache
+{
+public:
+   ~LottieFileCache();
+   static LottieFileCache &get() {
+      static LottieFileCache CACHE;
+
+      return CACHE;
+   }
+   std::shared_ptr<LOTModel> find(std::string &key);
+   void add(std::string &key, std::shared_ptr<LOTModel> value);
+private:
+   LottieFileCache(){}
+
+   std::unordered_map<std::string, std::shared_ptr<LOTModel>> mHash;
+
+};
+
+LottieFileCache::~LottieFileCache()
+{
+
+}
+std::shared_ptr<LOTModel>
+LottieFileCache::find(std::string &key)
+{
+   auto search = mHash.find(key);
+   if (search != mHash.end()) {
+       return search->second;
+   } else {
+      return nullptr;
+   }
+}
+
+void
+LottieFileCache::add(std::string &key, std::shared_ptr<LOTModel> value)
+{
+   mHash[key] = value;
+}
+
+LottieLoader::LottieLoader()
+{
+
+}
+
+bool LottieLoader::load(std::string &path)
+{
+   if (path.empty()) return false;
+
+   LottieFileCache &fileCache = LottieFileCache::get();
+
+   mModel = fileCache.find(path);
+   if (mModel)
+      return true;
+
+   std::ifstream f;
+   f.open(path);
+
+   if (!f.is_open()) {
+      printf("failed to open \n");
+      return false;
+   } else {
+      std::stringstream buf;
+      buf << f.rdbuf();
+
+      LottieParser parser(const_cast<char *>(buf.str().data()));
+      mModel = parser.model();
+      fileCache.add(path, mModel);
+
+      f.close();
+   }
+
+   return true;
+}
+
+std::shared_ptr<LOTModel> LottieLoader::model()
+{
+   return mModel;
+}
+
diff --git a/src/lottie/lottieloader.h b/src/lottie/lottieloader.h
new file mode 100644 (file)
index 0000000..0c4367b
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef LOTTIELOADER_H
+#define LOTTIELOADER_H
+
+#include<sstream>
+#include<memory>
+
+class LOTModel;
+class LottieLoader
+{
+public:
+   LottieLoader();
+   bool load(std::string &filePath);
+   std::shared_ptr<LOTModel> model();
+private:
+   std::shared_ptr<LOTModel>    mModel;
+};
+
+#endif // LOTTIELOADER_H
+
+
diff --git a/src/lottie/lottiemodel.cpp b/src/lottie/lottiemodel.cpp
new file mode 100644 (file)
index 0000000..e843008
--- /dev/null
@@ -0,0 +1,274 @@
+#include "lottiemodel.h"
+#include<stack>
+#include<cassert>
+
+
+
+class LottieRepeaterProcesser : public LOTDataVisitor
+{
+public:
+    LottieRepeaterProcesser():mRepeaterFound(false){}
+    void visit(LOTCompositionData *obj) {}
+    void visit(LOTLayerData *obj) {}
+    void visit(LOTTransformData *) {}
+    void visit(LOTShapeGroupData *obj) {}
+    void visit(LOTShapeData *) {}
+    void visit(LOTRectData *) {}
+    void visit(LOTEllipseData *) {}
+    void visit(LOTTrimData *) {}
+    void visit(LOTRepeaterData *) { mRepeaterFound = true;}
+    void visit(LOTFillData *) {}
+    void visit(LOTStrokeData *) {}
+    void visit(LOTPolystarData *) {}
+    void visitChildren(LOTGroupData *obj) {
+        for(auto child :obj->mChildren) {
+            child.get()->accept(this);
+            if (mRepeaterFound) {
+                LOTRepeaterData *repeater = static_cast<LOTRepeaterData *>(child.get());
+                std::shared_ptr<LOTShapeGroupData> sharedShapeGroup= std::make_shared<LOTShapeGroupData>();
+                LOTShapeGroupData *shapeGroup = sharedShapeGroup.get();
+                repeater->mChildren.push_back(sharedShapeGroup);
+                // copy all the child of the object till repeater and
+                // move that in to a group and then add that group to
+                // the repeater object.
+                for(auto cpChild :obj->mChildren) {
+                    if (cpChild == child)
+                        break;
+                    // there shouldn't be any trim object left in the child list
+                    if (cpChild.get()->type() == LOTData::Type::Trim) {
+                        assert(0);
+                    }
+                    shapeGroup->mChildren.push_back(cpChild);
+                }
+                mRepeaterFound = false;
+            }
+        }
+    }
+public:
+    bool mRepeaterFound;
+};
+
+class LottiePathOperationProcesser : public LOTDataVisitor
+{
+public:
+    LottiePathOperationProcesser():mPathOperator(false), mPathNode(false){}
+    void visit(LOTCompositionData *obj) {}
+    void visit(LOTLayerData *obj) {}
+    void visit(LOTTransformData *) {}
+    void visit(LOTShapeGroupData *obj) {}
+    void visit(LOTShapeData *) {mPathNode = true;}
+    void visit(LOTRectData *) {mPathNode = true;}
+    void visit(LOTEllipseData *) { mPathNode = true;}
+    void visit(LOTTrimData *) { mPathOperator = true;}
+    void visit(LOTRepeaterData *) {}
+    void visit(LOTFillData *) {}
+    void visit(LOTStrokeData *) {}
+    void visit(LOTPolystarData *) { mPathNode = true;}
+    void visitChildren(LOTGroupData *obj) {
+        int curOpCount = mPathOperationList.size();
+        mPathOperator = false;
+        mPathNode = false;
+        for (auto i = obj->mChildren.rbegin(); i != obj->mChildren.rend(); ++i) {
+            auto child = *i;
+            child.get()->accept(this);
+            if (mPathOperator) {
+                mPathOperationList.push_back(child);
+                //obj->mChildren.erase(std::next(i).base());
+            }
+            if (mPathNode) {
+               updatePathObject(static_cast<LOTPath *>(child.get()));
+            }
+            mPathOperator = false;
+            mPathNode = false;
+        }
+        mPathOperationList.erase(mPathOperationList.begin() + curOpCount, mPathOperationList.end());
+    }
+
+    void updatePathObject(LOTPath *drawable) {
+        for (auto i = mPathOperationList.rbegin(); i != mPathOperationList.rend(); ++i) {
+            drawable->mPathOperations.push_back(*i);
+        }
+    }
+public:
+    bool mPathOperator;
+    bool mPathNode;
+    std::vector<std::shared_ptr<LOTData>> mPathOperationList;
+};
+
+class LottiePaintOperationProcesser : public LOTDataVisitor
+{
+public:
+    LottiePaintOperationProcesser():mPaintOperator(false), mPathNode(false){}
+    void visit(LOTCompositionData *obj) {}
+    void visit(LOTLayerData *obj) {}
+    void visit(LOTTransformData *) {}
+    void visit(LOTShapeGroupData *obj) {}
+    void visit(LOTShapeData *) {mPathNode = true;}
+    void visit(LOTRectData *) {mPathNode = true;}
+    void visit(LOTEllipseData *) { mPathNode = true;}
+    void visit(LOTTrimData *) {}
+    void visit(LOTRepeaterData *) {}
+    void visit(LOTFillData *) { mPaintOperator = true;}
+    void visit(LOTStrokeData *) { mPaintOperator = true;}
+    void visit(LOTPolystarData *) { mPathNode = true;}
+    void visitChildren(LOTGroupData *obj) {
+        int curOpCount = mPaintOperationList.size();
+        mPaintOperator = false;
+        mPathNode = false;
+        for (auto i = obj->mChildren.rbegin(); i != obj->mChildren.rend(); ++i) {
+            auto child = *i;
+            child.get()->accept(this);
+            if (mPaintOperator) {
+                mPaintOperationList.push_back(child);
+                //obj->mChildren.erase(std::next(i).base());
+            }
+            if (mPathNode) {
+               // put it in the list
+               updatePathObject(static_cast<LOTPath *>(child.get()));
+            }
+            mPaintOperator = false;
+            mPathNode = false;
+        }
+        mPaintOperationList.erase(mPaintOperationList.begin() + curOpCount, mPaintOperationList.end());
+    }
+
+    void updatePathObject(LOTPath *drawable) {
+        for (auto i = mPaintOperationList.begin(); i != mPaintOperationList.end(); ++i) {
+            drawable->mPaintOperations.push_back(*i);
+        }
+    }
+public:
+    bool mPaintOperator;
+    bool mPathNode;
+    std::vector<std::shared_ptr<LOTData>> mPaintOperationList;
+};
+
+void LOTCompositionData::processRepeaterObjects()
+{
+    LottieRepeaterProcesser visitor;
+    accept(&visitor);
+}
+
+void LOTCompositionData::processPathOperatorObjects()
+{
+    LottiePathOperationProcesser visitor;
+    accept(&visitor);
+}
+
+void LOTCompositionData::processPaintOperatorObjects()
+{
+    LottiePaintOperationProcesser visitor;
+    accept(&visitor);
+}
+
+
+VMatrix LOTTransformData::matrix(int frameNo) const
+{
+    if (mStaticMatrix)
+        return mCachedMatrix;
+    else
+        return computeMatrix(frameNo);
+}
+
+float LOTTransformData::opacity(int frameNo) const
+{
+    return mOpacity.value(frameNo)/100.f;
+}
+
+void LOTTransformData::cacheMatrix()
+{
+    mCachedMatrix = computeMatrix(0);
+}
+
+VMatrix LOTTransformData::computeMatrix(int frameNo) const
+{
+    VMatrix m;
+    m.translate(mPosition.value(frameNo)).
+      rotate(mRotation.value(frameNo)).
+      scale(mScale.value(frameNo)/100.f).
+      translate(-mAnchor.value(frameNo));
+    return m;
+}
+
+int LOTStrokeData::getDashInfo(int frameNo, float *array) const
+{
+    if (!mDash.mDashCount) return 0;
+    // odd case
+    if (mDash.mDashCount % 2) {
+        for (int i = 0; i < mDash.mDashCount; i++) {
+            array[i] = mDash.mDashArray[i].value(frameNo);
+        }
+        return mDash.mDashCount;
+    } else { // even case when last gap info is not provided.
+        int i;
+        for (i = 0; i < mDash.mDashCount-1 ; i++) {
+            array[i] = mDash.mDashArray[i].value(frameNo);
+        }
+        array[i] = array[i-1];
+        array[i+1] = mDash.mDashArray[i].value(frameNo);
+        return mDash.mDashCount+1;
+    }
+}
+
+int LOTGStrokeData::getDashInfo(int frameNo, float *array) const
+{
+    if (!mDash.mDashCount) return 0;
+    // odd case
+    if (mDash.mDashCount % 2) {
+        for (int i = 0; i < mDash.mDashCount; i++) {
+            array[i] = mDash.mDashArray[i].value(frameNo);
+        }
+        return mDash.mDashCount;
+    } else { // even case when last gap info is not provided.
+        int i;
+        for (i = 0; i < mDash.mDashCount-1 ; i++) {
+            array[i] = mDash.mDashArray[i].value(frameNo);
+        }
+        array[i] = array[i-1];
+        array[i+1] = mDash.mDashArray[i].value(frameNo);
+        return mDash.mDashCount+1;
+    }
+}
+
+void LOTGradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
+{
+    bool init = false;
+    if (!grad) {
+        if (mGradientType == 1)
+            grad = std::unique_ptr<VLinearGradient>(new VLinearGradient(0,0,0,0));
+        else
+            grad = std::unique_ptr<VRadialGradient>(new VRadialGradient(0,0,0,0,0,0));
+        grad->mSpread = VGradient::Spread::Pad;
+        init = true;
+    }
+
+    if (!mGradient.isStatic() || init) {
+        LottieGradient gradData = mGradient.value(frameNo);
+        int size = gradData.mGradient.size();
+        float *ptr = gradData.mGradient.data();
+        grad->mStops.clear();
+        for (int i = 0; i < size ; i += 4) {
+            float stop = ptr[i];
+            LottieColor color = LottieColor(ptr[i+1], ptr[i+2], ptr[i+3]);
+            grad->mStops.push_back(std::make_pair(stop, color.toColor()));
+        }
+    }
+
+    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.fx = end.x();
+        grad->radial.fy = end.y();
+    }
+}
+
+
diff --git a/src/lottie/lottiemodel.h b/src/lottie/lottiemodel.h
new file mode 100644 (file)
index 0000000..7ae6b81
--- /dev/null
@@ -0,0 +1,755 @@
+#ifndef LOTModel_H
+#define LOTModel_H
+
+#include<vector>
+#include<memory>
+#include<unordered_map>
+#include"vpoint.h"
+#include"vrect.h"
+#include"vinterpolator.h"
+#include"vmatrix.h"
+#include"vbezier.h"
+#include"vbrush.h"
+#include"vpath.h"
+
+
+class LOTCompositionData;
+class LOTLayerData;
+class LOTTransformData;
+class LOTShapeGroupData;
+class LOTShapeData;
+class LOTRectData;
+class LOTEllipseData;
+class LOTTrimData;
+class LOTRepeaterData;
+class LOTFillData;
+class LOTStrokeData;
+class LOTGroupData;
+class LOTGFillData;
+class LOTGStrokeData;
+class LottieShapeData;
+class LOTPolystarData;
+class LOTMaskData;
+
+class LOTDataVisitor
+{
+public:
+    virtual ~LOTDataVisitor() {}
+    virtual void visit(LOTCompositionData *) = 0;
+    virtual void visit(LOTLayerData *) = 0;
+    virtual void visit(LOTTransformData *) = 0;
+    virtual void visit(LOTShapeGroupData *) = 0;
+    virtual void visit(LOTShapeData *) = 0;
+    virtual void visit(LOTRectData *) = 0;
+    virtual void visit(LOTEllipseData *) = 0;
+    virtual void visit(LOTPolystarData *) {};
+    virtual void visit(LOTTrimData *) = 0;
+    virtual void visit(LOTRepeaterData *) = 0;
+    virtual void visit(LOTFillData *) = 0;
+    virtual void visit(LOTStrokeData *) = 0;
+    virtual void visit(LOTGFillData *){};
+    virtual void visit(LOTGStrokeData *){};
+    virtual void visitChildren(LOTGroupData *) = 0;
+};
+
+enum class MatteType
+{
+    None = 0,
+    Alpha = 1,
+    AlphaInv,
+    Luma,
+    LumaInv
+};
+
+enum class LayerType {
+    Precomp = 0,
+    Solid = 1,
+    Image = 2,
+    Null = 3,
+    Shape = 4,
+    Text = 5
+};
+
+class LottieColor
+{
+public:
+    LottieColor():r(1),g(1), b(1){}
+    LottieColor(float red, float green , float blue):r(red), g(green),b(blue){}
+    VColor toColor(float a=1){ return VColor((255 * r), (255 * g), (255 * b), (255 * a));}
+    friend inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2);
+    friend inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2);
+public:
+    float r;
+    float g;
+    float b;
+};
+
+inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2)
+{
+    return LottieColor(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
+}
+inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2)
+{
+    return LottieColor(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
+}
+
+inline const LottieColor operator*(const LottieColor &c, float m)
+{ return LottieColor(c.r*m, c.g*m, c.b*m); }
+
+inline const LottieColor operator*(float m, const LottieColor &c)
+{ return LottieColor(c.r*m, c.g*m, c.b*m); }
+
+class LottieShapeData
+{
+public:
+    VPath toPath() const{
+        if (mPoints.empty()) return VPath();
+
+        VPath path;
+        int size = mPoints.size();
+        const VPointF *points = mPoints.data();
+        path.moveTo(points[0]);
+        for (int i = 1 ; i < size; i+=3) {
+           path.cubicTo(points[i], points[i+1], points[i+2]);
+        }
+        if (mClosed)
+          path.close();
+
+       return path;
+    }
+public:
+    std::vector<VPointF>    mPoints;
+    bool                     mClosed = false;   /* "c" */
+};
+
+
+template<typename T>
+class LOTKeyFrame
+{
+public:
+    LOTKeyFrame():mStartValue(),
+                     mEndValue(),
+                     mStartFrame(0),
+                     mEndFrame(0),
+                     mInterpolator(nullptr),
+                     mInTangent(),
+                     mOutTangent(),
+                     mPathKeyFrame(false){}
+
+    T value(int frameNo) const {
+        float progress = mInterpolator->value(float(frameNo - mStartFrame) / float(mEndFrame - mStartFrame));
+        return mStartValue + progress * (mEndValue - mStartValue);
+    }
+
+public:
+    T                   mStartValue;
+    T                   mEndValue;
+    int                 mStartFrame;
+    int                 mEndFrame;
+    std::shared_ptr<VInterpolator> mInterpolator;
+
+    /* this is for interpolating position along a path
+     * Need to move to other place because its only applicable
+     * for positional property.
+     */
+    VPointF            mInTangent;
+    VPointF            mOutTangent;
+    bool                mPathKeyFrame;
+};
+
+template<>
+class LOTKeyFrame<VPointF>
+{
+public:
+    LOTKeyFrame():mStartValue(),
+                  mEndValue(),
+                  mStartFrame(0),
+                  mEndFrame(0),
+                  mInterpolator(nullptr),
+                  mInTangent(),
+                  mOutTangent(),
+                  mPathKeyFrame(false){}
+
+    VPointF value(int frameNo) const {
+        float progress = mInterpolator->value(float(frameNo - mStartFrame) / float(mEndFrame - mStartFrame));
+        if (mPathKeyFrame & 1) {
+            return VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,  mEndValue + mInTangent, mEndValue).pointAt(progress);
+        } else {
+            return mStartValue + progress * (mEndValue - mStartValue);
+        }
+    }
+
+public:
+    VPointF                   mStartValue;
+    VPointF                   mEndValue;
+    int                        mStartFrame;
+    int                        mEndFrame;
+    std::shared_ptr<VInterpolator> mInterpolator;
+
+    /* this is for interpolating position along a path
+     * Need to move to other place because its only applicable
+     * for positional property.
+     */
+    VPointF            mInTangent;
+    VPointF            mOutTangent;
+    bool                mPathKeyFrame;
+};
+
+template<>
+class LOTKeyFrame<LottieShapeData>
+{
+public:
+    LOTKeyFrame():mStartValue(),
+                     mEndValue(),
+                     mStartFrame(0),
+                     mEndFrame(0),
+                     mInterpolator(nullptr),
+                     mInTangent(),
+                     mOutTangent(),
+                     mPathKeyFrame(false){}
+
+    LottieShapeData value(int frameNo) const {
+         float progress = mInterpolator->value(float(frameNo - mStartFrame) / float(mEndFrame - mStartFrame));
+
+         if (mStartValue.mPoints.size() != mEndValue.mPoints.size())
+            return LottieShapeData();
+
+         LottieShapeData result;
+         for (unsigned int i = 0 ; i < mStartValue.mPoints.size(); i++) {
+            result.mPoints.push_back(mStartValue.mPoints[i] + progress * (mEndValue.mPoints[i] - mStartValue.mPoints[i]));
+         }
+        return result;
+    }
+
+public:
+    LottieShapeData                   mStartValue;
+    LottieShapeData                   mEndValue;
+    int                 mStartFrame;
+    int                 mEndFrame;
+    std::shared_ptr<VInterpolator> mInterpolator;
+
+    /* this is for interpolating position along a path
+     * Need to move to other place because its only applicable
+     * for positional property.
+     */
+    VPointF            mInTangent;
+    VPointF            mOutTangent;
+    bool                mPathKeyFrame;
+};
+
+template<typename T>
+class LOTAnimInfo
+{
+public:
+    bool hasKeyFrame(int frameNo) {
+        if (frameNo >= mStartFrame && frameNo <= mEndFrame)
+            return true;
+        else
+            return false;
+    }
+    T value(int frameNo) const {
+        if (mStartFrame >= frameNo)
+            return mKeyFrames.front().mStartValue;
+        if(mEndFrame <= frameNo)
+            return mKeyFrames.back().mEndValue;
+
+        for(auto keyFrame : mKeyFrames) {
+            if (frameNo >= keyFrame.mStartFrame && frameNo <= keyFrame.mEndFrame)
+                return keyFrame.value(frameNo);
+        }
+        return T();
+    }
+public:
+    std::vector<LOTKeyFrame<T>>    mKeyFrames;
+    int                            mStartFrame;
+    int                            mEndFrame;
+};
+
+template<typename T>
+class LOTAnimatable
+{
+public:
+    LOTAnimatable():mValue(),mAnimInfo(nullptr){}
+    LOTAnimatable(const T &value): mValue(value){}
+    bool isStatic() const {if (mAnimInfo) return false; else return true;}
+    T value(int frameNo) const{
+        if (isStatic())
+            return mValue;
+        else
+            return mAnimInfo->value(frameNo);
+    }
+public:
+    T                                    mValue;
+    int                                  mPropertyIndex; /* "ix" */
+    std::shared_ptr<LOTAnimInfo<T>>   mAnimInfo;
+};
+
+enum class LottieBlendMode
+{
+    Normal = 0,
+    Multiply = 1,
+    Screen = 2,
+    OverLay = 3
+};
+
+class LOTDataVisitor;
+class LOTData
+{
+public:
+    enum class Type {
+        Composition = 1,
+        Layer,
+        ShapeGroup,
+        Transform,
+        Fill,
+        Stroke,
+        GFill,
+        GStroke,
+        Rect,
+        Ellipse,
+        Shape,
+        Polystar,
+        Trim,
+        Repeater
+    };
+    inline LOTData::Type type() const {return mType;}
+    virtual void accept(LOTDataVisitor *){}
+    virtual ~LOTData(){}
+    LOTData(LOTData::Type  type): mStatic(true), mType(type){}
+    bool isStatic() const{return mStatic;}
+    void setStatic(bool value) {mStatic = value;}
+    virtual bool hasChildren() {return false;}
+public:
+    bool                mStatic;
+    LOTData::Type  mType;
+};
+
+class LOTGroupData: public LOTData
+{
+public:
+    LOTGroupData(LOTData::Type  type):LOTData(type){}
+    virtual bool hasChildren() {return true;}
+public:
+    std::vector<std::shared_ptr<LOTData>>  mChildren;
+    std::shared_ptr<LOTTransformData>      mTransform;
+};
+
+class LOTShapeGroupData : public LOTGroupData
+{
+public:
+    void accept(LOTDataVisitor *visitor) override
+    {visitor->visit(this); visitor->visitChildren(this);}
+
+    LOTShapeGroupData():LOTGroupData(LOTData::Type::ShapeGroup){}
+};
+
+class LOTLayerData;
+class LOTAsset
+{
+public:
+    LOTAsset(){}
+    int                                          mAssetType; //lottie asset type  (precomp/char/image)
+    std::string                                  mRefId; // ref id
+    std::vector<std::shared_ptr<LOTData>>   mLayers;
+};
+
+class LOTCompositionData : public LOTGroupData
+{
+public:
+    void processPathOperatorObjects();
+    void processPaintOperatorObjects();
+    void processRepeaterObjects();
+    void accept(LOTDataVisitor *visitor) override
+    {visitor->visit(this); visitor->visitChildren(this);}
+    LOTCompositionData():LOTGroupData(LOTData::Type::Composition){}
+    inline long frameDuration()const{return mEndFrame - mStartFrame -1;}
+    inline long frameRate()const{return mFrameRate;}
+    inline long startFrame() const {return mStartFrame;}
+    inline long endFrame() const {return mEndFrame;}
+    inline VSize size() const { return mSize;}
+
+public:
+    std::string          mVersion;
+    VSize               mSize;
+    bool                 mAnimation = false;
+    long                 mStartFrame = 0;
+    long                 mEndFrame = 0;
+    float                mFrameRate;
+    LottieBlendMode      mBlendMode;
+    std::unordered_map<std::string,
+                       std::shared_ptr<VInterpolator>> mInterpolatorCache;
+    std::unordered_map<std::string,
+                       std::shared_ptr<LOTAsset>>    mAssets;
+};
+
+class LOTLayerData : public LOTGroupData
+{
+public:
+    void accept(LOTDataVisitor *visitor) override
+    {visitor->visit(this); visitor->visitChildren(this);}
+    LOTLayerData():LOTGroupData(LOTData::Type::Layer),
+                  mMatteType(MatteType::None),
+                  mParentId(-1),
+                  mId(-1),
+                  mHasPathOperator(false), mHasMask(false){}
+    inline bool hasPathOperator() const noexcept {return mHasPathOperator;}
+    inline int id() const noexcept{ return mId;}
+    inline int parentId() const noexcept{ return mParentId;}
+    inline int inFrame() const noexcept{return mInFrame;}
+    inline int outFrame() const noexcept{return mOutFrame;}
+    inline int startFrame() const noexcept{return mOutFrame;}
+    inline int solidWidth() const noexcept{return mSolidLayer.mWidth;}
+    inline int solidHeight() const noexcept{return mSolidLayer.mHeight;}
+    inline LottieColor solidColor() const noexcept{return mSolidLayer.mColor;}
+public:
+    MatteType            mMatteType;
+    VRect               mBound;
+    LayerType            mLayerType; //lottie layer type  (solid/shape/precomp)
+    int                  mParentId; // Lottie the id of the parent in the composition
+    int                  mId;  // Lottie the group id  used for parenting.
+    long                 mInFrame = 0;
+    long                 mOutFrame = 0;
+    long                 mStartFrame = 0;
+    LottieBlendMode      mBlendMode;
+    float                mTimeStreatch;
+    std::string          mPreCompRefId;
+    LOTAnimatable<float>     mTimeRemap;  /* "tm" */
+    struct SolidLayer {
+        int            mWidth;
+        int            mHeight;
+        LottieColor    mColor;
+    };
+    SolidLayer          mSolidLayer;
+    bool                mHasPathOperator;
+    bool                mHasMask;
+    std::vector<std::shared_ptr<LOTMaskData>>  mMasks;
+};
+
+class LOTTransformData : public LOTData
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTTransformData():LOTData(LOTData::Type::Transform),
+                      mRotation(0),
+                      mScale(VPointF(100, 100)),
+                      mPosition(VPointF(0, 0)),
+                      mAnchor(VPointF(0, 0)),
+                      mOpacity(100),
+                      mSkew(0),
+                      mSkewAxis(0),
+                      mStaticMatrix(true){}
+    VMatrix matrix(int frameNo) const;
+    float    opacity(int frameNo) const;
+    void cacheMatrix();
+    inline bool staticMatrix() const {return mStaticMatrix;}
+private:
+    VMatrix computeMatrix(int frameNo) const;
+public:
+    LOTAnimatable<float>     mRotation;  /* "r" */
+    LOTAnimatable<VPointF>  mScale;     /* "s" */
+    LOTAnimatable<VPointF>  mPosition;  /* "p" */
+    LOTAnimatable<VPointF>  mAnchor;    /* "a" */
+    LOTAnimatable<float>     mOpacity;   /* "o" */
+    LOTAnimatable<float>     mSkew;      /* "sk" */
+    LOTAnimatable<float>     mSkewAxis;  /* "sa" */
+    bool                     mStaticMatrix;
+    VMatrix                 mCachedMatrix;
+};
+
+class LOTFillData : public LOTData
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTFillData():LOTData(LOTData::Type::Fill), mFillRule(FillRule::Winding){}
+    inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
+    inline FillRule fillRule() const {return mFillRule;}
+public:
+    FillRule                       mFillRule; /* "r" */
+    LOTAnimatable<LottieColor>     mColor;   /* "c" */
+    LOTAnimatable<int>             mOpacity;  /* "o" */
+    bool                           mEnabled = true; /* "fillEnabled" */
+};
+
+struct LOTDashProperty
+{
+    LOTDashProperty():mDashCount(0), mStatic(true){}
+    LOTAnimatable<float>     mDashArray[5]; /* "d" "g" "o"*/
+    int                      mDashCount;
+    bool                     mStatic;
+};
+
+class LOTStrokeData : public LOTData
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTStrokeData():LOTData(LOTData::Type::Stroke){}
+    inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
+    inline float width(int frameNo) const {return mWidth.value(frameNo);}
+    inline CapStyle capStyle() const {return mCapStyle;}
+    inline JoinStyle joinStyle() const {return mJoinStyle;}
+    inline float meterLimit() const{return mMeterLimit;}
+    inline bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
+    int getDashInfo(int frameNo, float *array) const;
+public:
+    LOTAnimatable<LottieColor>        mColor;      /* "c" */
+    LOTAnimatable<int>                mOpacity;    /* "o" */
+    LOTAnimatable<float>              mWidth;      /* "w" */
+    CapStyle                          mCapStyle;   /* "lc" */
+    JoinStyle                         mJoinStyle;  /* "lj" */
+    float                             mMeterLimit; /* "ml" */
+    LOTDashProperty                   mDash;
+    bool                              mEnabled = true; /* "fillEnabled" */
+};
+
+class LottieGradient
+{
+public:
+    friend inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2);
+    friend inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2);
+    friend inline LottieGradient operator*(float m, const LottieGradient &g);
+public:
+    std::vector<float>    mGradient;
+};
+
+inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2)
+{
+    if (g1.mGradient.size() != g2.mGradient.size())
+        return g1;
+
+    LottieGradient newG;
+    newG.mGradient = g1.mGradient;
+
+    auto g2It = g2.mGradient.begin();
+    for(auto &i : newG.mGradient) {
+        i = i + *g2It;
+        g2It++;
+    }
+
+    return newG;
+}
+
+inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2)
+{
+    if (g1.mGradient.size() != g2.mGradient.size())
+        return g1;
+    LottieGradient newG;
+    newG.mGradient = g1.mGradient;
+
+    auto g2It = g2.mGradient.begin();
+    for(auto &i : newG.mGradient) {
+        i = i - *g2It;
+        g2It++;
+    }
+
+    return newG;
+}
+
+inline LottieGradient operator*(float m, const LottieGradient &g)
+{
+    LottieGradient newG;
+    newG.mGradient = g.mGradient;
+
+    for(auto &i : newG.mGradient) {
+        i = i * m;
+    }
+    return newG;
+}
+
+
+
+class LOTGradient : public LOTData
+{
+public:
+    LOTGradient(LOTData::Type  type):LOTData(type){}
+    inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
+    void update(std::unique_ptr<VGradient> &grad, int frameNo);
+public:
+    int                                 mGradientType;        /* "t" Linear=1 , Radial = 2*/
+    LOTAnimatable<VPointF>             mStartPoint;          /* "s" */
+    LOTAnimatable<VPointF>             mEndPoint;            /* "e" */
+    LOTAnimatable<int>                  mHighlightLength;     /* "h" */
+    LOTAnimatable<int>                  mHighlightAngle;      /* "a" */
+    LOTAnimatable<int>                  mOpacity;             /* "o" */
+    LOTAnimatable<LottieGradient>       mGradient;            /* "g" */
+    bool                                mEnabled = true;      /* "fillEnabled" */
+};
+
+class LOTGFillData : public LOTGradient
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTGFillData():LOTGradient(LOTData::Type::GFill), mFillRule(FillRule::Winding){}
+    inline FillRule fillRule() const {return mFillRule;}
+public:
+    FillRule                       mFillRule; /* "r" */
+};
+
+class LOTGStrokeData : public LOTGradient
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTGStrokeData():LOTGradient(LOTData::Type::GStroke){}
+    inline float width(int frameNo) const {return mWidth.value(frameNo);}
+    inline CapStyle capStyle() const {return mCapStyle;}
+    inline JoinStyle joinStyle() const {return mJoinStyle;}
+    inline float meterLimit() const{return mMeterLimit;}
+    inline bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
+    int getDashInfo(int frameNo, float *array) const;
+public:
+    LOTAnimatable<float>           mWidth;       /* "w" */
+    CapStyle                       mCapStyle;    /* "lc" */
+    JoinStyle                      mJoinStyle;   /* "lj" */
+    float                          mMeterLimit;  /* "ml" */
+    LOTDashProperty                mDash;
+};
+
+class LOTPath : public LOTData
+{
+public:
+    LOTPath(LOTData::Type  type):LOTData(type), mDirection(3){}
+    bool isDirectionCW() const { return ((mDirection == 3) ? false : true );}
+public:
+    int                                    mDirection;
+    std::vector<std::shared_ptr<LOTData>>  mPathOperations;
+    std::vector<std::shared_ptr<LOTData>>  mPaintOperations;
+};
+
+class LOTShapeData : public LOTPath
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    void process();
+    LOTShapeData():LOTPath(LOTData::Type::Shape){}
+public:
+    LOTAnimatable<LottieShapeData>    mShape;
+};
+
+class LOTMaskData
+{
+public:
+    enum class Mode {
+      None,
+      Add,
+      Substarct,
+      Intersect
+    };
+    LOTMaskData():mInv(false), mIsStatic(true){}
+    inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
+    inline bool isStatic() const {return mIsStatic;}
+public:
+    LOTAnimatable<LottieShapeData>    mShape;
+    LOTAnimatable<float>              mOpacity;
+    bool                              mInv;
+    bool                              mIsStatic;
+    LOTMaskData::Mode                 mMode;
+};
+
+class LOTRectData : public LOTPath
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTRectData():LOTPath(LOTData::Type::Rect),
+                       mPos(VPointF(0,0)),
+                       mSize(VPointF(0,0)),
+                       mRound(0){}
+public:
+    LOTAnimatable<VPointF>    mPos;
+    LOTAnimatable<VPointF>    mSize;
+    LOTAnimatable<float>       mRound;
+};
+
+class LOTEllipseData : public LOTPath
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTEllipseData():LOTPath(LOTData::Type::Ellipse),
+                          mPos(VPointF(0,0)),
+                          mSize(VPointF(0,0)){}
+public:
+    LOTAnimatable<VPointF>   mPos;
+    LOTAnimatable<VPointF>   mSize;
+};
+
+class LOTPolystarData : public LOTPath
+{
+public:
+    enum class PolyType {
+        Star = 1,
+        Polygon = 2
+    };
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    LOTPolystarData():LOTPath(LOTData::Type::Polystar),
+                          mType(PolyType::Polygon),
+                          mPos(VPointF(0,0)),
+                          mPointCount(0),
+                          mInnerRadius(0),
+                          mOuterRadius(0),
+                          mInnerRoundness(0),
+                          mOuterRoundness(0),
+                          mRotation(0){}
+public:
+    LOTPolystarData::PolyType     mType;
+    LOTAnimatable<VPointF>       mPos;
+    LOTAnimatable<float>          mPointCount;
+    LOTAnimatable<float>          mInnerRadius;
+    LOTAnimatable<float>          mOuterRadius;
+    LOTAnimatable<float>          mInnerRoundness;
+    LOTAnimatable<float>          mOuterRoundness;
+    LOTAnimatable<float>          mRotation;
+};
+
+class LOTTrimData : public LOTData
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this);}
+    enum class TrimType {
+        Simultaneously,
+        Individually
+    };
+    LOTTrimData():LOTData(LOTData::Type::Trim),
+                       mStart(0),
+                       mEnd(0),
+                       mOffset(0),
+                       mTrimType(TrimType::Simultaneously){}
+public:
+    LOTAnimatable<float>             mStart;
+    LOTAnimatable<float>             mEnd;
+    LOTAnimatable<float>             mOffset;
+    LOTTrimData::TrimType            mTrimType;
+};
+
+class LOTRepeaterData : public LOTGroupData
+{
+public:
+    void accept(LOTDataVisitor *visitor) final
+    {visitor->visit(this); visitor->visitChildren(this);}
+    LOTRepeaterData():LOTGroupData(LOTData::Type::Repeater),
+                           mCopies(0),
+                           mOffset(0){}
+public:
+    LOTAnimatable<float>             mCopies;
+    LOTAnimatable<float>             mOffset;
+};
+
+class LOTModel
+{
+public:
+   bool  isStatic() const{return mRoot->isStatic();}
+   int frameDuration() {return mRoot->frameDuration();}
+   int frameRate() {return mRoot->frameRate();}
+   int startFrame() {return mRoot->startFrame();}
+public:
+    std::shared_ptr<LOTCompositionData> mRoot;
+};
+
+#endif // LOTModel_H
diff --git a/src/lottie/lottieparser.cpp b/src/lottie/lottieparser.cpp
new file mode 100644 (file)
index 0000000..68b9314
--- /dev/null
@@ -0,0 +1,1870 @@
+
+#include "lottieparser.h"
+
+#define DEBUG_PARSER
+
+// This parser implements JSON token-by-token parsing with an API that is
+// more direct; we don't have to create  handler object and
+// callbacks. Instead, we retrieve values from the JSON stream by calling
+// GetInt(), GetDouble(), GetString() and GetBool(), traverse into structures
+// by calling EnterObject() and EnterArray(), and skip over unwanted data by
+// calling SkipValue(). As we know the lottie file structure this way will be the efficient way
+// of parsing the file.
+//
+// If you aren't sure of what's next in the JSON data, you can use PeekType() and
+// PeekValue() to look ahead to the next object before reading it.
+//
+// If you call the wrong retrieval method--e.g. GetInt when the next JSON token is
+// not an int, EnterObject or EnterArray when there isn't actually an object or array
+// to read--the stream parsing will end immediately and no more data will be delivered.
+//
+// After calling EnterObject, you retrieve keys via NextObjectKey() and values via
+// the normal getters. When NextObjectKey() returns null, you have exited the
+// object, or you can call SkipObject() to skip to the end of the object
+// immediately. If you fetch the entire object (i.e. NextObjectKey() returned  null),
+// you should not call SkipObject().
+//
+// After calling EnterArray(), you must alternate between calling NextArrayValue()
+// to see if the array has more data, and then retrieving values via the normal
+// getters. You can call SkipArray() to skip to the end of the array immediately.
+// If you fetch the entire array (i.e. NextArrayValue() returned null),
+// you should not call SkipArray().
+//
+// This parser uses in-situ strings, so the JSON buffer will be altered during the
+// parse.
+
+
+#include "rapidjson/document.h"
+#include <iostream>
+#include "lottiemodel.h"
+#include"velapsedtimer.h"
+
+
+RAPIDJSON_DIAG_PUSH
+#ifdef __GNUC__
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+using namespace rapidjson;
+
+class LookaheadParserHandler {
+public:
+    bool Null() { st_ = kHasNull; v_.SetNull(); return true; }
+    bool Bool(bool b) { st_ = kHasBool; v_.SetBool(b); return true; }
+    bool Int(int i) { st_ = kHasNumber; v_.SetInt(i); return true; }
+    bool Uint(unsigned u) { st_ = kHasNumber; v_.SetUint(u); return true; }
+    bool Int64(int64_t i) { st_ = kHasNumber; v_.SetInt64(i); return true; }
+    bool Uint64(uint64_t u) { st_ = kHasNumber; v_.SetUint64(u); return true; }
+    bool Double(double d) { st_ = kHasNumber; v_.SetDouble(d); return true; }
+    bool RawNumber(const char*, SizeType, bool) { return false; }
+    bool String(const char* str, SizeType length, bool) { st_ = kHasString; v_.SetString(str, length); return true; }
+    bool StartObject() { st_ = kEnteringObject; return true; }
+    bool Key(const char* str, SizeType length, bool) { st_ = kHasKey; v_.SetString(str, length); return true; }
+    bool EndObject(SizeType) { st_ = kExitingObject; return true; }
+    bool StartArray() { st_ = kEnteringArray; return true; }
+    bool EndArray(SizeType) { st_ = kExitingArray; return true; }
+
+protected:
+    LookaheadParserHandler(char* str);
+    void ParseNext();
+
+protected:
+    enum LookaheadParsingState {
+        kInit,
+        kError,
+        kHasNull,
+        kHasBool,
+        kHasNumber,
+        kHasString,
+        kHasKey,
+        kEnteringObject,
+        kExitingObject,
+        kEnteringArray,
+        kExitingArray
+    };
+
+    Value v_;
+    LookaheadParsingState st_;
+    Reader r_;
+    InsituStringStream ss_;
+
+    static const int parseFlags = kParseDefaultFlags | kParseInsituFlag;
+};
+
+
+class LottieParserImpl : protected LookaheadParserHandler {
+public:
+    LottieParserImpl(char* str) : LookaheadParserHandler(str) {}
+
+public:
+    bool EnterObject();
+    bool EnterArray();
+    const char* NextObjectKey();
+    bool NextArrayValue();
+    int GetInt();
+    double GetDouble();
+    const char* GetString();
+    bool GetBool();
+    void GetNull();
+
+    void SkipObject();
+    void SkipArray();
+    void SkipValue();
+    Value* PeekValue();
+    int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of object/array)
+
+    bool IsValid() { return st_ != kError; }
+
+    void Skip(const char *key);
+    VRect getRect();
+    LottieBlendMode getBlendMode();
+    CapStyle getLineCap();
+    JoinStyle getLineJoin();
+    FillRule getFillRule();
+    LOTTrimData::TrimType getTrimType();
+    MatteType getMatteType();
+    LayerType getLayerType();
+
+    std::shared_ptr<LOTCompositionData> composition() const {return mComposition;}
+    void parseComposition();
+    void parseAssets(LOTCompositionData *comp);
+    std::shared_ptr<LOTAsset> parseAsset();
+    void parseLayers(LOTCompositionData *comp);
+    std::shared_ptr<LOTData> parseLayer();
+    void parseMaskProperty(LOTLayerData *layer);
+    void parseShapesAttr(LOTLayerData *layer);
+    void parseObject(LOTGroupData *parent);
+    std::shared_ptr<LOTMaskData>  parseMaskObject();
+    std::shared_ptr<LOTData> parseObjectTypeAttr();
+    std::shared_ptr<LOTData> parseGroupObject();
+    std::shared_ptr<LOTData> parseRectObject();
+    std::shared_ptr<LOTData> parseEllipseObject();
+    std::shared_ptr<LOTData> parseShapeObject();
+    std::shared_ptr<LOTData> parsePolystarObject();
+
+    std::shared_ptr<LOTTransformData> parseTransformObject();
+    std::shared_ptr<LOTData> parseFillObject();
+    std::shared_ptr<LOTData> parseGFillObject();
+    std::shared_ptr<LOTData> parseStrokeObject();
+    std::shared_ptr<LOTData> parseGStrokeObject();
+    std::shared_ptr<LOTData> parseTrimObject();
+    std::shared_ptr<LOTData> parseReapeaterObject();
+
+
+    void parseGradientProperty(LOTGradient *gradient, const char *key);
+
+    VPointF parseInperpolatorPoint();
+    void parseArrayValue(VPointF &pt);
+    void parseArrayValue(LottieColor &pt);
+    void parseArrayValue(float &val);
+    void parseArrayValue(int &val);
+    void parseArrayValue(LottieGradient &gradient);
+    void getValue(VPointF &val);
+    void getValue(float &val);
+    void getValue(LottieColor &val);
+    void getValue(int &val);
+    void getValue(LottieShapeData &shape);
+    void getValue(LottieGradient &gradient);
+    template<typename T>
+    void parseKeyFrame(LOTAnimInfo<T> &obj);
+    template<typename T>
+    void parseProperty(LOTAnimatable<T> &obj);
+
+    void parseShapeKeyFrame(LOTAnimInfo<LottieShapeData> &obj);
+    void parseShapeProperty(LOTAnimatable<LottieShapeData> &obj);
+    void parseArrayValue(std::vector<VPointF> &v);
+    void parseDashProperty(LOTDashProperty &dash);
+
+    LottieColor toColor(const char *str);
+
+    void resolveLayerRefs();
+protected:
+    std::shared_ptr<LOTCompositionData>            mComposition;
+    LOTCompositionData                            *compRef;
+    LOTLayerData                                  *curLayerRef;
+    std::vector<std::shared_ptr<LOTLayerData>>     mLayersToUpdate;
+    void SkipOut(int depth);
+};
+
+LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), st_(kInit), r_(), ss_(str) {
+    r_.IterativeParseInit();
+    ParseNext();
+}
+
+void LookaheadParserHandler::ParseNext() {
+    if (r_.HasParseError()) {
+        st_ = kError;
+        return;
+    }
+
+    if (!r_.IterativeParseNext<parseFlags>(ss_, *this)) {
+        vCritical<<"Lottie file parsing error";
+        RAPIDJSON_ASSERT(0);
+    }
+}
+
+
+bool LottieParserImpl::EnterObject() {
+    if (st_ != kEnteringObject) {
+        st_  = kError;
+        RAPIDJSON_ASSERT(false);
+        return false;
+    }
+
+    ParseNext();
+    return true;
+}
+
+bool LottieParserImpl::EnterArray() {
+    if (st_ != kEnteringArray) {
+        st_  = kError;
+        RAPIDJSON_ASSERT(false);
+        return false;
+    }
+
+    ParseNext();
+    return true;
+}
+
+const char* LottieParserImpl::NextObjectKey() {
+    if (st_ == kHasKey) {
+        const char* result = v_.GetString();
+        ParseNext();
+        return result;
+    }
+
+    /* SPECIAL CASE
+     * The parser works with a prdefined rule that it will be only
+     * while (NextObjectKey()) for each object but in case of our nested group
+     * object we can call multiple time NextObjectKey() while exiting the object
+     * so ignore those and don't put parser in the error state.
+     * */
+    if (st_ == kExitingArray || st_ == kEnteringObject ) {
+// #ifdef DEBUG_PARSER
+//         vDebug<<"Object: Exiting nested loop";
+// #endif
+        return 0;
+    }
+
+    if (st_ != kExitingObject) {
+        RAPIDJSON_ASSERT(false);
+        st_ = kError;
+        return 0;
+    }
+
+    ParseNext();
+    return 0;
+}
+
+bool LottieParserImpl::NextArrayValue() {
+    if (st_ == kExitingArray) {
+        ParseNext();
+        return false;
+    }
+
+    /* SPECIAL CASE
+     * same as  NextObjectKey()
+     */
+    if (st_ == kExitingObject) {
+// #ifdef DEBUG_PARSER
+//         vDebug<<"Array: Exiting nested loop";
+// #endif
+        return 0;
+    }
+
+
+    if (st_ == kError || st_ == kHasKey) {
+        RAPIDJSON_ASSERT(false);
+        st_ = kError;
+        return false;
+    }
+
+    return true;
+}
+
+int LottieParserImpl::GetInt() {
+    if (st_ != kHasNumber || !v_.IsInt()) {
+        st_ = kError;
+        RAPIDJSON_ASSERT(false);
+        return 0;
+    }
+
+    int result = v_.GetInt();
+    ParseNext();
+    return result;
+}
+
+double LottieParserImpl::GetDouble() {
+    if (st_ != kHasNumber) {
+        st_  = kError;
+        RAPIDJSON_ASSERT(false);
+        return 0.;
+    }
+
+    double result = v_.GetDouble();
+    ParseNext();
+    return result;
+}
+
+bool LottieParserImpl::GetBool() {
+    if (st_ != kHasBool) {
+        st_  = kError;
+        RAPIDJSON_ASSERT(false);
+        return false;
+    }
+
+    bool result = v_.GetBool();
+    ParseNext();
+    return result;
+}
+
+void LottieParserImpl::GetNull() {
+    if (st_ != kHasNull) {
+        st_  = kError;
+        return;
+    }
+
+    ParseNext();
+}
+
+const char* LottieParserImpl::GetString() {
+    if (st_ != kHasString) {
+        st_  = kError;
+        RAPIDJSON_ASSERT(false);
+        return 0;
+    }
+
+    const char* result = v_.GetString();
+    ParseNext();
+    return result;
+}
+
+void LottieParserImpl::SkipOut(int depth) {
+    do {
+        if (st_ == kEnteringArray || st_ == kEnteringObject) {
+            ++depth;
+        }
+        else if (st_ == kExitingArray || st_ == kExitingObject) {
+            --depth;
+        }
+        else if (st_ == kError) {
+            RAPIDJSON_ASSERT(false);
+            return;
+        }
+
+        ParseNext();
+    }
+    while (depth > 0);
+}
+
+void LottieParserImpl::SkipValue() {
+    SkipOut(0);
+}
+
+void LottieParserImpl::SkipArray() {
+    SkipOut(1);
+}
+
+void LottieParserImpl::SkipObject() {
+    SkipOut(1);
+}
+
+Value* LottieParserImpl::PeekValue() {
+    if (st_ >= kHasNull && st_ <= kHasKey) {
+        return &v_;
+    }
+
+    return 0;
+}
+
+int LottieParserImpl::PeekType() {
+    if (st_ >= kHasNull && st_ <= kHasKey) {
+        return v_.GetType();
+    }
+
+    if (st_ == kEnteringArray) {
+        return kArrayType;
+    }
+
+    if (st_ == kEnteringObject) {
+        return kObjectType;
+    }
+
+    return -1;
+}
+
+void LottieParserImpl::Skip(const char *key)
+{
+    if (PeekType() == kArrayType) {
+        EnterArray();
+        SkipArray();
+    } else if (PeekType() == kObjectType) {
+        EnterObject();
+        SkipObject();
+    } else {
+        SkipValue();
+    }
+}
+
+LottieBlendMode
+LottieParserImpl::getBlendMode()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    LottieBlendMode mode = LottieBlendMode::Normal;
+
+    switch (GetInt()) {
+    case 1:
+        mode = LottieBlendMode::Multiply;
+        break;
+    case 2:
+        mode = LottieBlendMode::Screen;
+        break;
+    case 3:
+        mode = LottieBlendMode::OverLay;
+        break;
+    default:
+        break;
+    }
+    return mode;
+}
+VRect LottieParserImpl::getRect()
+{
+    VRect r;
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "l")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            r.setLeft(GetInt());
+        } else if (0 == strcmp(key, "r")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            r.setRight(GetInt());
+        } else if (0 == strcmp(key, "t")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            r.setTop(GetInt());
+        } else if (0 == strcmp(key, "b")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            r.setBottom(GetInt());
+        } else {
+            RAPIDJSON_ASSERT(false);
+        }
+    }
+    return r;
+}
+
+void LottieParserImpl::resolveLayerRefs()
+{
+    for(auto i : mLayersToUpdate) {
+        LOTLayerData *layer = i.get();
+        auto search = compRef->mAssets.find(layer->mPreCompRefId);
+        if (search != compRef->mAssets.end()) {
+           layer->mChildren = search->second.get()->mLayers;
+       }
+    }
+}
+
+
+void
+LottieParserImpl::parseComposition()
+{
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    EnterObject();
+    std::shared_ptr<LOTCompositionData> sharedComposition = std::make_shared<LOTCompositionData>();
+    LOTCompositionData *comp = sharedComposition.get();
+    compRef = comp;
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "v")) {
+            RAPIDJSON_ASSERT(PeekType() == kStringType);
+            comp->mVersion = std::string(GetString());
+        } else if (0 == strcmp(key, "w")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            comp->mSize.setWidth(GetInt());
+        } else if (0 == strcmp(key, "h")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            comp->mSize.setHeight(GetInt());
+        } else if (0 == strcmp(key, "ip")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            comp->mStartFrame = GetDouble();
+        } else if (0 == strcmp(key, "op")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            comp->mEndFrame = GetDouble();
+        } else if (0 == strcmp(key, "fr")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            comp->mFrameRate = GetDouble();
+        } else if (0 == strcmp(key, "assets")) {
+            parseAssets(comp);
+        } else if (0 == strcmp(key, "layers")) {
+            parseLayers(comp);
+        } else {
+#ifdef DEBUG_PARSER
+            vWarning<<"Composition Attribute Skipped : "<<key;
+#endif
+            Skip(key);
+        }
+    }
+    resolveLayerRefs();
+    // update the static property of Composition
+    bool staticFlag = true;
+    for (auto child : comp->mChildren) {
+        staticFlag &= child.get()->isStatic();
+    }
+    comp->setStatic(staticFlag);
+
+    mComposition = sharedComposition;
+}
+
+void LottieParserImpl::parseAssets(LOTCompositionData *composition)
+{
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        std::shared_ptr<LOTAsset> asset = parseAsset();
+        composition->mAssets[asset->mRefId] = asset;
+    }
+    // update the precomp layers with the actual layer object
+
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
+ *
+ */
+std::shared_ptr<LOTAsset>
+LottieParserImpl::parseAsset()
+{
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    std::shared_ptr<LOTAsset> sharedAsset = std::make_shared<LOTAsset>();
+    LOTAsset *asset = sharedAsset.get();
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "ty")) {    /* Type of layer: Shape. Value 4.*/
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            asset->mAssetType = GetInt();
+        } else if (0 == strcmp(key, "id")) {    /* reference id*/
+            RAPIDJSON_ASSERT(PeekType() == kStringType);
+            asset->mRefId = std::string(GetString());
+        }else if (0 == strcmp(key, "layers")) {
+            RAPIDJSON_ASSERT(PeekType() == kArrayType);
+            EnterArray();
+            while (NextArrayValue()) {
+                std::shared_ptr<LOTData> layer = parseLayer();
+                asset->mLayers.push_back(layer);
+            }
+        } else {
+    #ifdef DEBUG_PARSER
+            vWarning<<"Asset Attribute Skipped : "<<key;
+    #endif
+            Skip(key);
+        }
+    }
+    return sharedAsset;
+}
+
+void LottieParserImpl::parseLayers(LOTCompositionData *composition)
+{
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        std::shared_ptr<LOTData> layer = parseLayer();
+        composition->mChildren.push_back(layer);
+    }
+}
+
+LottieColor
+LottieParserImpl::toColor(const char *str)
+{
+   LottieColor color;
+
+   RAPIDJSON_ASSERT(strlen(str) == 7);
+   RAPIDJSON_ASSERT(str[0] == '#');
+
+   char tmp[3] = { '\0', '\0', '\0' };
+   tmp[0] = str[1]; tmp[1] = str[2];
+   color.r = std::strtol(tmp, NULL, 16)/ 255.0;
+
+   tmp[0] = str[3]; tmp[1] = str[4];
+   color.g = std::strtol(tmp, NULL, 16)/ 255.0;
+
+   tmp[0] = str[5]; tmp[1] = str[6];
+   color.b = std::strtol(tmp, NULL, 16)/ 255.0;
+
+   return color;
+}
+
+MatteType
+LottieParserImpl::getMatteType()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    switch (GetInt()) {
+    case 1:
+        return MatteType::Alpha;
+        break;
+    case 2:
+        return MatteType::AlphaInv;
+        break;
+    case 3:
+        return MatteType::Luma;
+        break;
+    case 4:
+        return MatteType::LumaInv;
+        break;
+    default:
+        return MatteType::None;
+        break;
+    }
+}
+
+LayerType LottieParserImpl::getLayerType()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    switch (GetInt()) {
+    case 0:
+        return LayerType::Precomp;
+        break;
+    case 1:
+        return LayerType::Solid;
+        break;
+    case 2:
+        return LayerType::Image;
+        break;
+    case 3:
+        return LayerType::Null;
+        break;
+    case 4:
+        return LayerType::Shape;
+        break;
+    case 5:
+        return LayerType::Text;
+        break;
+    default:
+        return LayerType::Null;
+        break;
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
+ *
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseLayer()
+{
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    std::shared_ptr<LOTLayerData> sharedLayer = std::make_shared<LOTLayerData>();
+    LOTLayerData *layer = sharedLayer.get();
+    curLayerRef = layer;
+    bool hasLayerRef = false;
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "ty")) {    /* Type of layer*/
+            layer->mLayerType = getLayerType();
+        } else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for parenting and expressions.*/
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            layer->mId = GetInt();
+        } else if (0 == strcmp(key, "parent")) { /*Layer Parent. Uses "ind" of parent.*/
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            layer->mParentId = GetInt();
+        } else if (0 == strcmp(key, "refId")) { /*preComp Layer reference id*/
+            RAPIDJSON_ASSERT(PeekType() == kStringType);
+            layer->mPreCompRefId = std::string(GetString());
+            mLayersToUpdate.push_back(sharedLayer);
+            hasLayerRef = true;
+        }else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching"
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            layer->mTimeStreatch = GetDouble();
+        } else if (0 == strcmp(key, "tm")) { // time remapping
+            parseProperty(layer->mTimeRemap);
+        }else if (0 == strcmp(key, "ip")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            layer->mInFrame = std::round(GetDouble());
+        } else if (0 == strcmp(key, "op")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            layer->mOutFrame = std::round(GetDouble());
+        }  else if (0 == strcmp(key, "st")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            layer->mStartFrame = GetDouble();
+        } else if (0 == strcmp(key, "bounds")) {
+            layer->mBound = getRect();
+        } else if (0 == strcmp(key, "bm")) {
+            layer->mBlendMode = getBlendMode();
+        } else if (0 == strcmp(key, "ks")) {
+            RAPIDJSON_ASSERT(PeekType() == kObjectType);
+            EnterObject();
+            layer->mTransform = parseTransformObject();
+        } else if (0 == strcmp(key, "shapes")) {
+            parseShapesAttr(layer);
+        } else if (0 == strcmp(key, "sw")) {
+            layer->mSolidLayer.mWidth = GetInt();
+        } else if (0 == strcmp(key, "sh")) {
+            layer->mSolidLayer.mHeight = GetInt();
+        } else if (0 == strcmp(key, "sc")) {
+            layer->mSolidLayer.mColor = toColor(GetString());
+        } else if (0 == strcmp(key, "tt")) {
+            layer->mMatteType = getMatteType();
+        } else if (0 == strcmp(key, "tt")) {
+            layer->mMatteType = getMatteType();
+        } else if (0 == strcmp(key, "hasMask")) {
+            layer->mHasMask = GetBool();
+        } else if (0 == strcmp(key, "masksProperties")) {
+            parseMaskProperty(layer);
+        }else {
+    #ifdef DEBUG_PARSER
+            vWarning<<"Layer Attribute Skipped : "<<key;
+    #endif
+            Skip(key);
+        }
+    }
+    // update the static property of layer
+    bool staticFlag = true;
+    for (auto child : layer->mChildren) {
+        staticFlag &= child.get()->isStatic();
+    }
+
+    for (auto mask : layer->mMasks) {
+        staticFlag &= mask->isStatic();
+    }
+
+    layer->setStatic(staticFlag &&
+                     layer->mTransform->isStatic() &&
+                     !hasLayerRef);
+
+    return sharedLayer;
+}
+
+void LottieParserImpl::parseMaskProperty(LOTLayerData *layer)
+{
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        layer->mMasks.push_back(parseMaskObject());
+    }
+}
+
+std::shared_ptr<LOTMaskData>
+LottieParserImpl::parseMaskObject()
+{
+    std::shared_ptr<LOTMaskData> sharedMask = std::make_shared<LOTMaskData>();
+    LOTMaskData *obj = sharedMask.get();
+
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "inv")) {
+            obj->mInv = GetBool();
+        } else if (0 == strcmp(key, "mode")) {
+            const char *str = GetString();
+            switch (str[0]) {
+            case 'n':
+                obj->mMode = LOTMaskData::Mode::None;
+                break;
+            case 'a':
+                obj->mMode = LOTMaskData::Mode::Add;
+                break;
+            case 's':
+                obj->mMode = LOTMaskData::Mode::Substarct;
+                break;
+            case 'i':
+                obj->mMode = LOTMaskData::Mode::Intersect;
+                break;
+            default:
+                obj->mMode = LOTMaskData::Mode::None;
+                break;
+            }
+        } else if (0 == strcmp(key, "pt")) {
+            parseShapeProperty(obj->mShape);
+        } else if (0 == strcmp(key, "o")) {
+            parseProperty(obj->mOpacity);
+        } else {
+            Skip(key);
+        }
+    }
+    obj->mIsStatic = obj->mShape.isStatic() && obj->mOpacity.isStatic();
+    return sharedMask;
+}
+
+
+
+void LottieParserImpl::parseShapesAttr(LOTLayerData *layer)
+{
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        parseObject(layer);
+    }
+}
+
+std::shared_ptr<LOTData>
+LottieParserImpl::parseObjectTypeAttr()
+{
+    RAPIDJSON_ASSERT(PeekType() == kStringType);
+    const char *type = GetString();
+    if (0 == strcmp(type, "gr")) {
+        return parseGroupObject();
+    } else if (0 == strcmp(type, "rc")) {
+        return parseRectObject();
+    } else if (0 == strcmp(type, "el")) {
+        return parseEllipseObject();
+    } else if (0 == strcmp(type, "tr")) {
+        return parseTransformObject();
+    } else if (0 == strcmp(type, "fl")) {
+        return parseFillObject();
+    } else if (0 == strcmp(type, "st")) {
+        return parseStrokeObject();
+    } else if (0 == strcmp(type, "gf")) {
+        return parseGFillObject();
+    } else if (0 == strcmp(type, "gs")) {
+        return parseGStrokeObject();
+    } else if (0 == strcmp(type, "sh")) {
+        return parseShapeObject();
+    } else if (0 == strcmp(type, "sr")) {
+        return parsePolystarObject();
+    } else if (0 == strcmp(type, "tm")) {
+        return parseTrimObject();
+    } else if (0 == strcmp(type, "rp")) {
+        return parseReapeaterObject();
+    } else {
+#ifdef DEBUG_PARSER
+        vDebug<<"The Object Type not yet handled = "<< type;
+#endif
+        return nullptr;
+    }
+}
+
+void
+LottieParserImpl::parseObject(LOTGroupData *parent)
+{
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "ty")) {
+            auto child = parseObjectTypeAttr();
+            if (child) {
+                if (child)
+                parent->mChildren.push_back(child);
+            }
+        } else {
+            Skip(key);
+        }
+    }
+}
+
+std::shared_ptr<LOTData>
+LottieParserImpl::parseGroupObject()
+{
+    std::shared_ptr<LOTShapeGroupData> sharedGroup = std::make_shared<LOTShapeGroupData>();
+
+    LOTShapeGroupData *group = sharedGroup.get();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "it")) {
+            RAPIDJSON_ASSERT(PeekType() == kArrayType);
+            EnterArray();
+            while (NextArrayValue()) {
+                RAPIDJSON_ASSERT(PeekType() == kObjectType);
+                parseObject(group);
+            }
+            group->mTransform = std::dynamic_pointer_cast<LOTTransformData>(group->mChildren.back());
+            group->mChildren.pop_back();
+        } else {
+            Skip(key);
+        }
+    }
+    bool staticFlag = true;
+    for (auto child : group->mChildren) {
+        staticFlag &= child.get()->isStatic();
+    }
+
+    group->setStatic(staticFlag &&
+                     group->mTransform->isStatic());
+
+    return sharedGroup;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseRectObject()
+{
+    std::shared_ptr<LOTRectData> sharedRect = std::make_shared<LOTRectData>();
+    LOTRectData *obj = sharedRect.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "p")) {
+            parseProperty(obj->mPos);
+        } else if (0 == strcmp(key, "s")) {
+            parseProperty(obj->mSize);
+        } else if (0 == strcmp(key, "r")) {
+            parseProperty(obj->mRound);
+        } else if (0 == strcmp(key, "d")) {
+            obj->mDirection = GetInt();
+        } else {
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mPos.isStatic() &&
+                   obj->mSize.isStatic() &&
+                   obj->mRound.isStatic());
+    return sharedRect;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/ellipse.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseEllipseObject()
+{
+    std::shared_ptr<LOTEllipseData> sharedEllipse = std::make_shared<LOTEllipseData>();
+    LOTEllipseData *obj = sharedEllipse.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "p")) {
+            parseProperty(obj->mPos);
+        } else if (0 == strcmp(key, "s")) {
+            parseProperty(obj->mSize);
+        } else if (0 == strcmp(key, "d")) {
+            obj->mDirection = GetInt();
+        } else {
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mPos.isStatic() &&
+                   obj->mSize.isStatic());
+    return sharedEllipse;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/shape.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseShapeObject()
+{
+    std::shared_ptr<LOTShapeData> sharedShape = std::make_shared<LOTShapeData>();
+    LOTShapeData *obj = sharedShape.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "ks")) {
+            parseShapeProperty(obj->mShape);
+        } else if (0 == strcmp(key, "d")) {
+            obj->mDirection = GetInt();
+        } else {
+#ifdef DEBUG_PARSER
+            vDebug<<"Shape property ignored :"<<key;
+#endif
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mShape.isStatic());
+
+    return sharedShape;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/star.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parsePolystarObject()
+{
+    std::shared_ptr<LOTPolystarData> sharedPolystar = std::make_shared<LOTPolystarData>();
+    LOTPolystarData *obj = sharedPolystar.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "p")) {
+            parseProperty(obj->mPos);
+        } else if (0 == strcmp(key, "pt")) {
+            parseProperty(obj->mPointCount);
+        } else if (0 == strcmp(key, "ir")) {
+            parseProperty(obj->mInnerRadius);
+        } else if (0 == strcmp(key, "is")) {
+            parseProperty(obj->mInnerRoundness);
+        } else if (0 == strcmp(key, "or")) {
+            parseProperty(obj->mOuterRadius);
+        } else if (0 == strcmp(key, "os")) {
+            parseProperty(obj->mOuterRoundness);
+        } else if (0 == strcmp(key, "r")) {
+            parseProperty(obj->mRotation);
+        } else if (0 == strcmp(key, "sy")) {
+            int starType = GetInt();
+            if (starType == 1)
+                obj->mType = LOTPolystarData::PolyType::Star;
+            if (starType == 2)
+                obj->mType = LOTPolystarData::PolyType::Polygon;
+        } else if (0 == strcmp(key, "d")) {
+            obj->mDirection = GetInt();
+        } else {
+#ifdef DEBUG_PARSER
+            vDebug<<"Polystar property ignored :"<<key;
+#endif
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mPos.isStatic() &&
+                   obj->mPointCount.isStatic() &&
+                   obj->mInnerRadius.isStatic() &&
+                   obj->mInnerRoundness.isStatic() &&
+                   obj->mOuterRadius.isStatic() &&
+                   obj->mOuterRoundness.isStatic() &&
+                   obj->mRotation.isStatic());
+
+    return sharedPolystar;
+}
+
+LOTTrimData::TrimType
+LottieParserImpl::getTrimType()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    switch (GetInt()) {
+    case 1:
+        return LOTTrimData::TrimType::Simultaneously;
+        break;
+    case 2:
+        return LOTTrimData::TrimType::Individually;
+        break;
+    default:
+        RAPIDJSON_ASSERT(0);
+        break;
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/trim.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseTrimObject()
+{
+    std::shared_ptr<LOTTrimData> sharedTrim = std::make_shared<LOTTrimData>();
+    LOTTrimData *obj = sharedTrim.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "s")) {
+            parseProperty(obj->mStart);
+        } else if (0 == strcmp(key, "e")) {
+            parseProperty(obj->mEnd);
+        } else if (0 == strcmp(key, "o")) {
+            parseProperty(obj->mOffset);
+        }  else if (0 == strcmp(key, "m")) {
+            obj->mTrimType = getTrimType();
+        } else {
+#ifdef DEBUG_PARSER
+            vDebug<<"Trim property ignored :"<<key;
+#endif
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mStart.isStatic() &&
+                   obj->mEnd.isStatic() &&
+                   obj->mOffset.isStatic());
+    curLayerRef->mHasPathOperator = true;
+    return sharedTrim;
+}
+
+std::shared_ptr<LOTData>
+LottieParserImpl::parseReapeaterObject()
+{
+    std::shared_ptr<LOTRepeaterData> sharedRepeater = std::make_shared<LOTRepeaterData>();
+    LOTRepeaterData *obj = sharedRepeater.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "c")) {
+            parseProperty(obj->mCopies);
+        } else if (0 == strcmp(key, "o")) {
+            parseProperty(obj->mOffset);
+        } else if (0 == strcmp(key, "tr")) {
+            obj->mTransform = parseTransformObject();
+        } else {
+#ifdef DEBUG_PARSER
+            vDebug<<"Repeater property ignored :"<<key;
+#endif
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mCopies.isStatic() &&
+                   obj->mOffset.isStatic() &&
+                   obj->mTransform->isStatic());
+
+    return sharedRepeater;
+}
+
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/transform.json
+ */
+std::shared_ptr<LOTTransformData>
+LottieParserImpl::parseTransformObject()
+{
+    std::shared_ptr<LOTTransformData> sharedTransform = std::make_shared<LOTTransformData>();
+    LOTTransformData *obj = sharedTransform.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "a")) {
+            parseProperty(obj->mAnchor);
+        } else if (0 == strcmp(key, "p")) {
+            parseProperty(obj->mPosition);
+        } else if (0 == strcmp(key, "r")) {
+            parseProperty(obj->mRotation);
+        } else if (0 == strcmp(key, "s")) {
+            parseProperty(obj->mScale);
+        } else if (0 == strcmp(key, "sk")) {
+            parseProperty(obj->mSkew);
+        }  else if (0 == strcmp(key, "sa")) {
+            parseProperty(obj->mSkewAxis);
+        } else if (0 == strcmp(key, "o")) {
+            parseProperty(obj->mOpacity);
+        } else {
+            Skip(key);
+        }
+    }
+    obj->mStaticMatrix = obj->mAnchor.isStatic() &&
+                         obj->mPosition.isStatic() &&
+                         obj->mRotation.isStatic() &&
+                         obj->mScale.isStatic() &&
+                         obj->mSkew.isStatic() &&
+                         obj->mSkewAxis.isStatic();
+    obj->setStatic(obj->mStaticMatrix && obj->mOpacity.isStatic() );
+
+    if (obj->mStaticMatrix)
+        obj->cacheMatrix();
+
+    return sharedTransform;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/fill.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseFillObject()
+{
+    std::shared_ptr<LOTFillData> sharedFill = std::make_shared<LOTFillData>();
+    LOTFillData *obj = sharedFill.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "c")) {
+            parseProperty(obj->mColor);
+        } else if (0 == strcmp(key, "o")) {
+            parseProperty(obj->mOpacity);
+        } else if (0 == strcmp(key, "fillEnabled")) {
+            obj->mEnabled = GetBool();
+        } else if (0 == strcmp(key, "r")) {
+            obj->mFillRule = getFillRule();
+        } else {
+#ifdef DEBUG_PARSER
+            vWarning<<"Fill property skipped = "<<key;
+#endif
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mColor.isStatic() &&
+                   obj->mOpacity.isStatic());
+
+    return sharedFill;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineCap.json
+ */
+CapStyle LottieParserImpl::getLineCap()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    switch (GetInt()) {
+    case 1:
+        return CapStyle::Flat;
+        break;
+    case 2:
+        return CapStyle::Round;
+        break;
+    default:
+        return CapStyle::Square;
+        break;
+    }
+}
+
+FillRule LottieParserImpl::getFillRule()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    switch (GetInt()) {
+    case 1:
+        return FillRule::Winding;
+        break;
+    case 2:
+        return FillRule::EvenOdd;
+        break;
+    default:
+        return FillRule::Winding;
+        break;
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineJoin.json
+ */
+JoinStyle LottieParserImpl::getLineJoin()
+{
+    RAPIDJSON_ASSERT(PeekType() == kNumberType);
+    switch (GetInt()) {
+    case 1:
+        return JoinStyle::Miter;
+        break;
+    case 2:
+        return JoinStyle::Round;
+        break;
+    default:
+        return JoinStyle::Bevel;
+        break;
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/stroke.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseStrokeObject()
+{
+    std::shared_ptr<LOTStrokeData> sharedStroke = std::make_shared<LOTStrokeData>();
+    LOTStrokeData *obj = sharedStroke.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "c")) {
+            parseProperty(obj->mColor);
+        } else if (0 == strcmp(key, "o")) {
+            parseProperty(obj->mOpacity);
+        } else if (0 == strcmp(key, "w")) {
+            parseProperty(obj->mWidth);
+        } else if (0 == strcmp(key, "fillEnabled")) {
+            obj->mEnabled = GetBool();
+        } else if (0 == strcmp(key, "lc")) {
+            obj->mCapStyle = getLineCap();
+        } else if (0 == strcmp(key, "lj")) {
+            obj->mJoinStyle = getLineJoin();
+        } else if (0 == strcmp(key, "ml")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            obj->mMeterLimit = GetDouble();
+        } else if (0 == strcmp(key, "d")) {
+            parseDashProperty(obj->mDash);
+        } else {
+#ifdef DEBUG_PARSER
+            vWarning<<"Stroke property skipped = "<<key;
+#endif
+            Skip(key);
+        }
+    }
+    obj->setStatic(obj->mColor.isStatic() &&
+                   obj->mOpacity.isStatic() &&
+                   obj->mWidth.isStatic() &&
+                   obj->mDash.mStatic);
+    return sharedStroke;
+}
+
+void
+LottieParserImpl::parseGradientProperty(LOTGradient *obj, const char *key)
+{
+    if (0 == strcmp(key, "t")) {
+        RAPIDJSON_ASSERT(PeekType() == kNumberType);
+        obj->mGradientType = GetInt();
+    } else if (0 == strcmp(key, "o")) {
+        parseProperty(obj->mOpacity);
+    } else if (0 == strcmp(key, "s")) {
+        parseProperty(obj->mStartPoint);
+    } else if (0 == strcmp(key, "e")) {
+        parseProperty(obj->mEndPoint);
+    } else if (0 == strcmp(key, "h")) {
+        parseProperty(obj->mHighlightLength);
+    } else if (0 == strcmp(key, "a")) {
+        parseProperty(obj->mHighlightAngle);
+    } else if (0 == strcmp(key, "g")) {
+        EnterObject();
+        while (const char* key = NextObjectKey()) {
+            if (0 == strcmp(key, "k")) {
+                parseProperty(obj->mGradient);
+            } else {
+                Skip(nullptr);
+            }
+        }
+    } else {
+#ifdef DEBUG_PARSER
+            vWarning<<"Gradient property skipped = "<<key;
+#endif
+            Skip(key);
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gfill.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseGFillObject()
+{
+    std::shared_ptr<LOTGFillData> sharedGFill = std::make_shared<LOTGFillData>();
+    LOTGFillData *obj = sharedGFill.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "r")) {
+            obj->mFillRule = getFillRule();
+        } else {
+            parseGradientProperty(obj, key);
+        }
+    }
+
+    return sharedGFill;
+}
+
+void LottieParserImpl::parseDashProperty(LOTDashProperty &dash)
+{
+    dash.mDashCount = 0;
+    dash.mStatic = true;
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        RAPIDJSON_ASSERT(PeekType() == kObjectType);
+        EnterObject();
+        while (const char* key = NextObjectKey()) {
+            if (0 == strcmp(key, "v")) {
+                parseProperty(dash.mDashArray[dash.mDashCount++]);
+            } else {
+                Skip(key);
+            }
+        }
+    }
+
+    // update the staic proprty
+    for (int i = 0 ; i < dash.mDashCount ; i++) {
+        if (!dash.mDashArray[i].isStatic()) {
+            dash.mStatic = false;
+            break;
+        }
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gstroke.json
+ */
+std::shared_ptr<LOTData>
+LottieParserImpl::parseGStrokeObject()
+{
+    std::shared_ptr<LOTGStrokeData> sharedGStroke = std::make_shared<LOTGStrokeData>();
+    LOTGStrokeData *obj = sharedGStroke.get();
+
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "w")) {
+            parseProperty(obj->mWidth);
+        } else if (0 == strcmp(key, "lc")) {
+            obj->mCapStyle = getLineCap();
+        } else if (0 == strcmp(key, "lj")) {
+            obj->mJoinStyle = getLineJoin();
+        } else if (0 == strcmp(key, "ml")) {
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            obj->mMeterLimit = GetDouble();
+        } else if (0 == strcmp(key, "d")) {
+            parseDashProperty(obj->mDash);
+        }  else {
+            parseGradientProperty(obj, key);
+        }
+    }
+
+    obj->setStatic(obj->mWidth.isStatic() &&
+                   obj->mDash.mStatic);
+    return sharedGStroke;
+}
+
+void LottieParserImpl::parseArrayValue(LottieColor &color)
+{
+    float val[4];
+    int i=0;
+    while (NextArrayValue()) {
+        val[i++] = GetDouble();
+    }
+
+    color.r = val[0];
+    color.g = val[1];
+    color.b = val[2];
+}
+
+void LottieParserImpl::parseArrayValue(VPointF &pt)
+{
+    float val[4];
+    int i=0;
+    while (NextArrayValue()) {
+        val[i++] = GetDouble();
+    }
+    pt.setX(val[0]);
+    pt.setY(val[1]);
+}
+
+void LottieParserImpl::parseArrayValue(float &val)
+{
+    RAPIDJSON_ASSERT(0);
+    val = GetDouble();
+}
+
+void LottieParserImpl::parseArrayValue(int &val)
+{
+    RAPIDJSON_ASSERT(0);
+    val = GetInt();
+}
+
+void LottieParserImpl::parseArrayValue(std::vector<VPointF> &v)
+{
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        RAPIDJSON_ASSERT(PeekType() == kArrayType);
+        EnterArray();
+        VPointF pt;
+        parseArrayValue(pt);
+        v.push_back(pt);
+    }
+}
+
+void LottieParserImpl::getValue(VPointF &pt)
+{
+    float val[4];
+    int i=0;
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        val[i++] = GetDouble();
+    }
+    pt.setX(val[0]);
+    pt.setY(val[1]);
+}
+
+void LottieParserImpl::getValue(float &val)
+{
+    if (PeekType() == kArrayType) {
+        EnterArray();
+        while (NextArrayValue()) {
+            val = GetDouble();
+        }
+    } else if (PeekType() == kNumberType) {
+        val = GetDouble();
+    } else {
+        RAPIDJSON_ASSERT(0);
+    }
+}
+
+void LottieParserImpl::getValue(LottieColor &color)
+{
+    float val[4];
+    int i=0;
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        val[i++] = GetDouble();
+    }
+    color.r = val[0];
+    color.g = val[1];
+    color.b = val[2];
+}
+
+void LottieParserImpl::parseArrayValue(LottieGradient &grad)
+{
+    while (NextArrayValue()) {
+        grad.mGradient.push_back(GetDouble());
+    }
+}
+
+void LottieParserImpl::getValue(LottieGradient &grad)
+{
+    RAPIDJSON_ASSERT(PeekType() == kArrayType);
+    EnterArray();
+    while (NextArrayValue()) {
+        grad.mGradient.push_back(GetDouble());
+    }
+}
+
+void LottieParserImpl::getValue(int &val)
+{
+    if (PeekType() == kArrayType) {
+        EnterArray();
+        while (NextArrayValue()) {
+            val = GetInt();
+        }
+    } else if (PeekType() == kNumberType) {
+        val = GetInt();
+    } else {
+        RAPIDJSON_ASSERT(0);
+    }
+}
+
+void LottieParserImpl::getValue(LottieShapeData &obj)
+{
+    std::vector<VPointF>    inPoint;          /* "i" */
+    std::vector<VPointF>    outPoint;         /* "o" */
+    std::vector<VPointF>    vertices;         /* "v" */
+    std::vector<VPointF>    points;
+    bool                     closed=false;
+
+    /*
+     * The shape object could be wrapped by a array
+     * if its part of the keyframe object
+     */
+    bool arrayWrapper = (PeekType() == kArrayType);
+    if (arrayWrapper)
+         EnterArray();
+
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "i")) {
+            parseArrayValue(inPoint);
+        } else if (0 == strcmp(key, "o")) {
+            parseArrayValue(outPoint);
+        } else if (0 == strcmp(key, "v")) {
+            parseArrayValue(vertices);
+        } else if (0 == strcmp(key, "c")) {
+            closed = GetBool();
+        }else {
+            RAPIDJSON_ASSERT(0);
+            Skip(nullptr);
+        }
+    }
+    // exit properly from the array
+    if (arrayWrapper)
+        NextArrayValue();
+
+
+/*
+ * Convert the AE shape format to
+ * list of bazier curves
+ * The final structure will be Move +size*Cubic + Cubic (if the path is closed one)
+ */
+    if (inPoint.size() != outPoint.size() ||
+        inPoint.size() != vertices.size()) {
+        vCritical<<"The Shape data are corrupted";
+        points = std::vector<VPointF>();
+    } else {
+        int size = vertices.size();
+        points.reserve(3*size + 4);
+        points.push_back(vertices[0]);
+        for (int i =1; i <size ; i++ ) {
+            points.push_back(vertices[i-1] + outPoint[i-1]); // CP1 = start + outTangent
+            points.push_back(vertices[i] + inPoint[i]); // CP2 = end + inTangent
+            points.push_back(vertices[i]); //end point
+        }
+
+        if (closed) {
+            points.push_back(vertices[size-1] + outPoint[size-1]); // CP1 = start + outTangent
+            points.push_back(vertices[0] + inPoint[0]); // CP2 = end + inTangent
+            points.push_back(vertices[0]); //end point
+        }
+    }
+    obj.mPoints = std::move(points);
+    obj.mClosed = closed;
+}
+
+VPointF
+LottieParserImpl::parseInperpolatorPoint()
+{
+    VPointF cp;
+    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+    EnterObject();
+    while(const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "x")) {
+            if (PeekType() == kNumberType) {
+                cp.setX(GetDouble());
+            } else {
+                RAPIDJSON_ASSERT(PeekType() == kArrayType);
+                EnterArray();
+                while (NextArrayValue()) {
+                    cp.setX(GetDouble());
+                }
+            }
+        }
+        if (0 == strcmp(key, "y")) {
+            if (PeekType() == kNumberType) {
+                cp.setY(GetDouble());
+            } else {
+                RAPIDJSON_ASSERT(PeekType() == kArrayType);
+                EnterArray();
+                while (NextArrayValue()) {
+                    cp.setY(GetDouble());
+                }
+            }
+        }
+    }
+    return cp;
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/multiDimensionalKeyframed.json
+ */
+template<typename T>
+void LottieParserImpl::parseKeyFrame(LOTAnimInfo<T> &obj)
+{
+    EnterObject();
+    LOTKeyFrame<T> keyframe;
+    VPointF inTangent;
+    VPointF outTangent;
+    const char *interpolatorKey = nullptr;
+    bool hold = false;
+     while (const char* key = NextObjectKey()) {
+         if (0 == strcmp(key, "i")) {
+             inTangent = parseInperpolatorPoint();
+         } else if (0 == strcmp(key, "o")) {
+             outTangent = parseInperpolatorPoint();
+         } else if (0 == strcmp(key, "n")) {
+             if (PeekType() == kStringType) {
+                interpolatorKey = GetString();
+             } else {
+                RAPIDJSON_ASSERT(PeekType() == kArrayType);
+                EnterArray();
+                while (NextArrayValue()) {
+                    RAPIDJSON_ASSERT(PeekType() == kStringType);
+                    interpolatorKey = GetString();
+                }
+             }
+             continue;
+         } else if (0 == strcmp(key, "t")) {
+             keyframe.mStartFrame = GetDouble();
+         } else if (0 == strcmp(key, "s")) {
+             getValue(keyframe.mStartValue);
+             continue;
+         } else if (0 == strcmp(key, "e")) {
+             getValue(keyframe.mEndValue);
+             continue;
+         } else if (0 == strcmp(key, "ti")) {
+             keyframe.mPathKeyFrame = true;
+             getValue(keyframe.mInTangent);
+             continue;
+         } else if (0 == strcmp(key, "to")) {
+             keyframe.mPathKeyFrame = true;
+             getValue(keyframe.mOutTangent);
+             continue;
+         } else if (0 == strcmp(key, "h")) {
+             hold = GetInt();
+             continue;
+         } else {
+#ifdef DEBUG_PARSER
+             vDebug<<"key frame property skipped = "<<key;
+#endif
+             Skip(key);
+         }
+     }
+
+     if (!obj.mKeyFrames.empty()) {
+         // update the endFrame value of current keyframe
+         obj.mKeyFrames.back().mEndFrame = keyframe.mStartFrame;
+     }
+
+     if (hold) {
+         interpolatorKey = "hold_interpolator";
+         inTangent = VPointF();
+         outTangent = VPointF();
+         keyframe.mEndValue = keyframe.mStartValue;
+     }
+
+     // Try to find the interpolator from cache
+     if (interpolatorKey) {
+         auto search = compRef->mInterpolatorCache.find(interpolatorKey);
+         if (search != compRef->mInterpolatorCache.end()) {
+             keyframe.mInterpolator = search->second;
+         } else {
+             keyframe.mInterpolator = std::make_shared<VInterpolator>(VInterpolator(inTangent, outTangent));
+             compRef->mInterpolatorCache[interpolatorKey] = keyframe.mInterpolator;
+         }
+     } else {
+         /* this is the last key frame. update the first and last frame of this property */
+         obj.mStartFrame = obj.mKeyFrames.front().mStartFrame;
+         obj.mEndFrame = keyframe.mStartFrame;
+         return;
+     }
+   obj.mKeyFrames.push_back(keyframe);
+}
+
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shapeKeyframed.json
+ */
+
+/*
+ * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shape.json
+ */
+void
+LottieParserImpl::parseShapeProperty(LOTAnimatable<LottieShapeData> &obj)
+{
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "k")) {
+            if (PeekType() == kArrayType) {
+                EnterArray();
+                while (NextArrayValue()) {
+                    RAPIDJSON_ASSERT(PeekType() == kObjectType);
+                    if (!obj.mAnimInfo)
+                        obj.mAnimInfo = std::make_shared<LOTAnimInfo<LottieShapeData>>();
+                    parseKeyFrame(*obj.mAnimInfo.get());
+                }
+            } else {
+                getValue(obj.mValue);
+            }
+        } else {
+#ifdef DEBUG_PARSER
+            vDebug<<"shape property ignored = "<<key;
+#endif
+            Skip(nullptr);
+        }
+    }
+}
+
+/*
+ * https://github.com/airbnb/lottie-web/tree/master/docs/json/properties
+ */
+template<typename T>
+void LottieParserImpl::parseProperty(LOTAnimatable<T> &obj)
+{
+    EnterObject();
+    while (const char* key = NextObjectKey()) {
+        if (0 == strcmp(key, "k")) {
+            if (PeekType() == kNumberType) {
+                /*single value property with no animation*/
+                getValue(obj.mValue);
+            } else {
+                RAPIDJSON_ASSERT(PeekType() == kArrayType);
+                EnterArray();
+                while (NextArrayValue()) {
+                    /* property with keyframe info*/
+                    if (PeekType() == kObjectType) {
+                        if (!obj.mAnimInfo)
+                            obj.mAnimInfo = std::make_shared<LOTAnimInfo<T>>();
+                        parseKeyFrame(*obj.mAnimInfo.get());
+                    } else {
+                        /* Read before modifying.
+                         * as there is no way of knowing if the
+                         * value of the array is either array of numbers
+                         * or array of object without entering the array
+                         * thats why this hack is there
+                         */
+                        RAPIDJSON_ASSERT(PeekType() == kNumberType);
+                        /*multi value property with no animation*/
+                        parseArrayValue(obj.mValue);
+                        /*break here as we already reached end of array*/
+                        break;
+                    }
+                }
+            }
+        }  else if (0 == strcmp(key, "ix")){
+            RAPIDJSON_ASSERT(PeekType() == kNumberType);
+            obj.mPropertyIndex = GetInt();
+        } else {
+            Skip(key);
+        }
+    }
+}
+
+class LOTDataInspector : public LOTDataVisitor
+{
+public:
+    void visit(LOTCompositionData *obj) {
+        vDebug<<"[COMP_START:: static:"<<obj->isStatic()<<" v:"<<obj->mVersion<<" [{ stFm endFm fmRate } { "<<obj->mStartFrame<<" "<<obj->mEndFrame<<" }]\n";
+    }
+    void visit(LOTLayerData *obj) {
+        vDebug<<"[LAYER_START:: type:"<<layerType(obj->mLayerType)<<" id:"<<obj->mId<<" Pid:"<<obj->mParentId
+               <<" static:"<<obj->isStatic()<<"[{ stFm endFm stTm tmStrch } { "
+               <<obj->mInFrame<<" "<<obj->mOutFrame<<" "<<obj->mStartFrame<<" "<<obj->mTimeStreatch <<" }]";
+    }
+    void visit(LOTTransformData *t) {
+        vDebug<<"[TRANSFORM: static: "<<t->isStatic()<<" ]";
+    }
+    void visit(LOTShapeGroupData *o) {
+        vDebug<<"[GROUP_START:: static:"<<o->isStatic()<<"]";
+    }
+    void visit(LOTShapeData *s) {
+        vDebug<<"[SHAPE: static:"<<s->isStatic()<<"]";
+    }
+    void visit(LOTRectData *r) {
+        vDebug<<"[RECT: static:"<<r->isStatic()<<"]";
+    }
+    void visit(LOTEllipseData *e) {
+        vDebug<<"[ELLIPSE: static:"<<e->isStatic()<<"]";
+    }
+    void visit(LOTTrimData *t) {
+        vDebug<<"[TRIM: static: "<<t->isStatic()<<" ]";
+    }
+    void visit(LOTRepeaterData *r) {
+        vDebug<<"[REPEATER: static:"<<r->isStatic()<<"]";
+    }
+    void visit(LOTFillData *f) {
+        vDebug<<"[FILL: static:"<<f->isStatic()<<"]";
+    }
+    void visit(LOTGFillData *f) {
+        vDebug<<"[GFILL: static:"<<f->isStatic()<<" ty:"<<f->mGradientType<<" s:"<<f->mStartPoint.value(0)<<" e:"<<f->mEndPoint.value(0)<<"]";
+    }
+    void visit(LOTGStrokeData *f) {
+        vDebug<<"[GSTROKE: static:"<<f->isStatic()<<"]";
+    }
+    void visit(LOTStrokeData *s) {
+        vDebug<<"[STROKE: static:"<<s->isStatic()<<"]";
+    }
+    void visitChildren(LOTGroupData *obj) {
+        for(auto child :obj->mChildren)
+            child.get()->accept(this);
+        switch (obj->type()) {
+        case LOTData::Type::Layer:
+        {
+            LOTLayerData *layer = static_cast<LOTLayerData *>(obj);
+            vDebug<<"[LAYER_END:: type:"<<layerType(layer->mLayerType).c_str()<<" id:"<<layer->mId<<"\n";
+            break;
+        }
+        case LOTData::Type::ShapeGroup:
+            vDebug<<"[GROUP_END]";
+            break;
+        case LOTData::Type::Composition:
+            vDebug<<"[COMP End ]\n";
+            break;
+        case LOTData::Type::Repeater:
+            vDebug<<"[REPEATER End ]";
+            break;
+        default:
+            break;
+        }
+    }
+    std::string layerType(LayerType type) {
+        switch (type) {
+        case LayerType::Precomp:
+            return "Precomp";
+            break;
+        case LayerType::Null:
+            return "Null";
+            break;
+        case LayerType::Shape:
+            return "Shape";
+            break;
+        case LayerType::Solid:
+            return "Solid";
+            break;
+        case LayerType::Image:
+            return "Image";
+            break;
+        case LayerType::Text:
+            return "Text";
+            break;
+        default:
+            return "Unknow";
+            break;
+        }
+    }
+};
+
+LottieParser::~LottieParser()
+{
+    delete d;
+}
+
+LottieParser::LottieParser(char* str): d(new LottieParserImpl(str))
+{
+    d->parseComposition();
+}
+
+std::shared_ptr<LOTModel> LottieParser::model()
+{
+    std::shared_ptr<LOTModel> model= std::make_shared<LOTModel>();
+     model->mRoot = d->composition();
+     model->mRoot->processPathOperatorObjects();
+     model->mRoot->processRepeaterObjects();
+
+#ifdef DEBUG_PARSER
+     LOTDataInspector inspector;
+     model->mRoot->accept(&inspector);
+#endif
+
+     return model;
+}
+
+RAPIDJSON_DIAG_POP
diff --git a/src/lottie/lottieparser.h b/src/lottie/lottieparser.h
new file mode 100644 (file)
index 0000000..bde8f39
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef LOTTIEPARSER_H
+#define LOTTIEPARSER_H
+
+#include "lottiemodel.h"
+
+class LottieParserImpl;
+class LottieParser {
+public:
+    ~LottieParser();
+    LottieParser(char* str);
+    std::shared_ptr<LOTModel> model();
+private:
+   LottieParserImpl   *d;
+};
+
+#endif // LOTTIEPARSER_H
diff --git a/src/lottie/lottieplayer.cpp b/src/lottie/lottieplayer.cpp
new file mode 100644 (file)
index 0000000..2356b95
--- /dev/null
@@ -0,0 +1,191 @@
+#include <lottieplayer.h>
+
+#include "lottiemodel.h"
+#include "lottieloader.h"
+#include "lottieitem.h"
+
+#include<fstream>
+
+
+class LOTPlayerPrivate
+{
+public:
+   LOTPlayerPrivate();
+   bool setFilePath(std::string path);
+   void setSize(const VSize &sz);
+   void size(int &w, int &h) const;
+   void setFrameRate(int frameRate);
+   int frameRate() const;
+   float playTime() const;
+   bool seek(float pos);
+   const std::vector<LOTNode *>& renderList()const;
+   bool render(float pos, const LOTBuffer &buffer);
+public:
+   std::string                     mFilePath;
+   std::shared_ptr<LOTModel>       mModel;
+   std::unique_ptr<LOTCompItem>    mCompItem;
+   VSize                          mSize;
+   int                             mFrameRate;
+};
+
+void LOTPlayerPrivate::setSize(const VSize &sz)
+{
+    mCompItem->resize(sz);
+}
+
+void LOTPlayerPrivate::size(int &w, int &h) const
+{
+    VSize size = mCompItem->size();
+    w = size.width();
+    h = size.height();
+}
+
+
+const std::vector<LOTNode *>& LOTPlayerPrivate::renderList() const
+{
+    return mCompItem->renderList();
+}
+int LOTPlayerPrivate::frameRate() const
+{
+   if (mFrameRate)
+      return mFrameRate;
+   else
+      return mModel->frameRate();
+}
+
+void LOTPlayerPrivate::setFrameRate(int frameRate)
+{
+   mFrameRate = frameRate;
+}
+
+float LOTPlayerPrivate::playTime() const
+{
+   if (mModel->isStatic()) return 0;
+
+   float fr = frameRate();
+   float fd = mModel->frameDuration();
+   return fd/fr;
+}
+
+bool LOTPlayerPrivate::seek(float pos)
+{
+   if (!mModel || !mCompItem) return false;
+
+   if (pos > 1.0) pos = 1.0;
+   if (pos < 0) pos = 0;
+   if (mModel->isStatic()) pos = 0;
+   int frameNumber = mModel->startFrame() + pos * mModel->frameDuration();
+   return mCompItem->update(frameNumber);
+}
+
+bool LOTPlayerPrivate::render(float pos, const LOTBuffer &buffer)
+{
+    if (seek(pos)) {
+        if (mCompItem->render(buffer))
+            return true;
+        else
+            return false;
+    } else {
+        return false;
+    }
+}
+
+
+LOTPlayerPrivate::LOTPlayerPrivate():mFrameRate(0)
+{
+
+}
+
+bool
+LOTPlayerPrivate::setFilePath(std::string path)
+{
+   LottieLoader loader;
+   if (loader.load(path)) {
+      mModel = loader.model();
+      mCompItem =  std::unique_ptr<LOTCompItem>(new LOTCompItem(mModel.get()));
+      return true;
+   }
+   return false;
+}
+
+
+LOTPlayer::LOTPlayer():d(new LOTPlayerPrivate())
+{
+
+}
+
+LOTPlayer::~LOTPlayer()
+{
+   delete d;
+}
+
+LOTPlayer::LOTPlayer(const char *filePath):d(new LOTPlayerPrivate())
+{
+   d->setFilePath(filePath);
+}
+
+/**
+ * \breif Brief abput the Api.
+ * Description about the setFilePath Api
+ * @param path  add the details
+ */
+
+bool LOTPlayer::setFilePath(const char *filePath)
+{
+   return d->setFilePath(filePath);
+}
+
+void LOTPlayer::setSize(int width, int height)
+{
+   d->setSize(VSize(width, height));
+}
+
+void LOTPlayer::size(int &width, int &height) const
+{
+   d->size(width, height);
+}
+
+float LOTPlayer::playTime() const
+{
+   return d->playTime();
+}
+
+int LOTPlayer::frameRate() const
+{
+   return d->frameRate();
+}
+
+void LOTPlayer::setFrameRate(int frameRate)
+{
+   d->setFrameRate(frameRate);
+}
+
+void LOTPlayer::seek(float pos)
+{
+   d->seek(pos);
+}
+
+const std::vector<LOTNode *>& LOTPlayer::renderList()const
+{
+    return d->renderList();
+}
+
+std::future<bool> LOTPlayer::render(float pos, const LOTBuffer &buffer)
+{
+    return std::async(std::launch::async, &LOTPlayerPrivate::render, d, pos, buffer);
+}
+
+bool LOTPlayer::renderSync(float pos, const LOTBuffer &buffer)
+{
+    return d->render(pos, buffer);
+}
+
+LOTNode::~LOTNode()
+{
+}
+
+LOTNode::LOTNode()
+{
+}
+
+
diff --git a/src/lottie/meson.build b/src/lottie/meson.build
new file mode 100644 (file)
index 0000000..ba61f07
--- /dev/null
@@ -0,0 +1,12 @@
+
+source_file  = files('lottieparser.cpp')
+source_file += files('lottieloader.cpp')
+source_file += files('lottiemodel.cpp')
+source_file += files('lottieplayer.cpp')
+source_file += files('lottieitem.cpp')
+
+
+lottie_dep = declare_dependency(
+                                  include_directories : include_directories('.'),
+                                  sources : source_file
+                               )
diff --git a/src/lottie/rapidjson/allocators.h b/src/lottie/rapidjson/allocators.h
new file mode 100644 (file)
index 0000000..655f4a3
--- /dev/null
@@ -0,0 +1,271 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ALLOCATORS_H_
+#define RAPIDJSON_ALLOCATORS_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Allocator
+
+/*! \class rapidjson::Allocator
+    \brief Concept for allocating, resizing and freeing memory block.
+    
+    Note that Malloc() and Realloc() are non-static but Free() is static.
+    
+    So if an allocator need to support Free(), it needs to put its pointer in 
+    the header of memory block.
+
+\code
+concept Allocator {
+    static const bool kNeedFree;    //!< Whether this allocator needs to call Free().
+
+    // Allocate a memory block.
+    // \param size of the memory block in bytes.
+    // \returns pointer to the memory block.
+    void* Malloc(size_t size);
+
+    // Resize a memory block.
+    // \param originalPtr The pointer to current memory block. Null pointer is permitted.
+    // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
+    // \param newSize the new size in bytes.
+    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
+
+    // Free a memory block.
+    // \param pointer to the memory block. Null pointer is permitted.
+    static void Free(void *ptr);
+};
+\endcode
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+// CrtAllocator
+
+//! C-runtime library allocator.
+/*! This class is just wrapper for standard C library memory routines.
+    \note implements Allocator concept
+*/
+class CrtAllocator {
+public:
+    static const bool kNeedFree = true;
+    void* Malloc(size_t size) { 
+        if (size) //  behavior of malloc(0) is implementation defined.
+            return std::malloc(size);
+        else
+            return NULL; // standardize to returning NULL.
+    }
+    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+        (void)originalSize;
+        if (newSize == 0) {
+            std::free(originalPtr);
+            return NULL;
+        }
+        return std::realloc(originalPtr, newSize);
+    }
+    static void Free(void *ptr) { std::free(ptr); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryPoolAllocator
+
+//! Default memory allocator used by the parser and DOM.
+/*! This allocator allocate memory blocks from pre-allocated memory chunks. 
+
+    It does not free memory blocks. And Realloc() only allocate new memory.
+
+    The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
+
+    User may also supply a buffer as the first chunk.
+
+    If the user-buffer is full then additional chunks are allocated by BaseAllocator.
+
+    The user-buffer is not deallocated by this allocator.
+
+    \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
+    \note implements Allocator concept
+*/
+template <typename BaseAllocator = CrtAllocator>
+class MemoryPoolAllocator {
+public:
+    static const bool kNeedFree = false;    //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
+
+    //! Constructor with chunkSize.
+    /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+        \param baseAllocator The allocator for allocating memory chunks.
+    */
+    MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : 
+        chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+    {
+    }
+
+    //! Constructor with user-supplied buffer.
+    /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
+
+        The user buffer will not be deallocated when this allocator is destructed.
+
+        \param buffer User supplied buffer.
+        \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
+        \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+        \param baseAllocator The allocator for allocating memory chunks.
+    */
+    MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+        chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+    {
+        RAPIDJSON_ASSERT(buffer != 0);
+        RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
+        chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
+        chunkHead_->capacity = size - sizeof(ChunkHeader);
+        chunkHead_->size = 0;
+        chunkHead_->next = 0;
+    }
+
+    //! Destructor.
+    /*! This deallocates all memory chunks, excluding the user-supplied buffer.
+    */
+    ~MemoryPoolAllocator() {
+        Clear();
+        RAPIDJSON_DELETE(ownBaseAllocator_);
+    }
+
+    //! Deallocates all memory chunks, excluding the user-supplied buffer.
+    void Clear() {
+        while (chunkHead_ && chunkHead_ != userBuffer_) {
+            ChunkHeader* next = chunkHead_->next;
+            baseAllocator_->Free(chunkHead_);
+            chunkHead_ = next;
+        }
+        if (chunkHead_ && chunkHead_ == userBuffer_)
+            chunkHead_->size = 0; // Clear user buffer
+    }
+
+    //! Computes the total capacity of allocated memory chunks.
+    /*! \return total capacity in bytes.
+    */
+    size_t Capacity() const {
+        size_t capacity = 0;
+        for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+            capacity += c->capacity;
+        return capacity;
+    }
+
+    //! Computes the memory blocks allocated.
+    /*! \return total used bytes.
+    */
+    size_t Size() const {
+        size_t size = 0;
+        for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+            size += c->size;
+        return size;
+    }
+
+    //! Allocates a memory block. (concept Allocator)
+    void* Malloc(size_t size) {
+        if (!size)
+            return NULL;
+
+        size = RAPIDJSON_ALIGN(size);
+        if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
+            if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
+                return NULL;
+
+        void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
+        chunkHead_->size += size;
+        return buffer;
+    }
+
+    //! Resizes a memory block (concept Allocator)
+    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+        if (originalPtr == 0)
+            return Malloc(newSize);
+
+        if (newSize == 0)
+            return NULL;
+
+        originalSize = RAPIDJSON_ALIGN(originalSize);
+        newSize = RAPIDJSON_ALIGN(newSize);
+
+        // Do not shrink if new size is smaller than original
+        if (originalSize >= newSize)
+            return originalPtr;
+
+        // Simply expand it if it is the last allocation and there is sufficient space
+        if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
+            size_t increment = static_cast<size_t>(newSize - originalSize);
+            if (chunkHead_->size + increment <= chunkHead_->capacity) {
+                chunkHead_->size += increment;
+                return originalPtr;
+            }
+        }
+
+        // Realloc process: allocate and copy memory, do not free original buffer.
+        if (void* newBuffer = Malloc(newSize)) {
+            if (originalSize)
+                std::memcpy(newBuffer, originalPtr, originalSize);
+            return newBuffer;
+        }
+        else
+            return NULL;
+    }
+
+    //! Frees a memory block (concept Allocator)
+    static void Free(void *ptr) { (void)ptr; } // Do nothing
+
+private:
+    //! Copy constructor is not permitted.
+    MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
+    //! Copy assignment operator is not permitted.
+    MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
+
+    //! Creates a new chunk.
+    /*! \param capacity Capacity of the chunk in bytes.
+        \return true if success.
+    */
+    bool AddChunk(size_t capacity) {
+        if (!baseAllocator_)
+            ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
+        if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
+            chunk->capacity = capacity;
+            chunk->size = 0;
+            chunk->next = chunkHead_;
+            chunkHead_ =  chunk;
+            return true;
+        }
+        else
+            return false;
+    }
+
+    static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
+
+    //! Chunk header for perpending to each chunk.
+    /*! Chunks are stored as a singly linked list.
+    */
+    struct ChunkHeader {
+        size_t capacity;    //!< Capacity of the chunk in bytes (excluding the header itself).
+        size_t size;        //!< Current size of allocated memory in bytes.
+        ChunkHeader *next;  //!< Next chunk in the linked list.
+    };
+
+    ChunkHeader *chunkHead_;    //!< Head of the chunk linked-list. Only the head chunk serves allocation.
+    size_t chunk_capacity_;     //!< The minimum capacity of chunk when they are allocated.
+    void *userBuffer_;          //!< User supplied buffer.
+    BaseAllocator* baseAllocator_;  //!< base allocator for allocating memory chunks.
+    BaseAllocator* ownBaseAllocator_;   //!< base allocator created by this object.
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/src/lottie/rapidjson/cursorstreamwrapper.h b/src/lottie/rapidjson/cursorstreamwrapper.h
new file mode 100644 (file)
index 0000000..52c11a7
--- /dev/null
@@ -0,0 +1,78 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
+#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
+
+#include "stream.h"
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4702)  // unreachable code
+RAPIDJSON_DIAG_OFF(4512)  // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+
+//! Cursor stream wrapper for counting line and column number if error exists.
+/*!
+    \tparam InputStream     Any stream that implements Stream Concept
+*/
+template <typename InputStream, typename Encoding = UTF8<> >
+class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
+public:
+    typedef typename Encoding::Ch Ch;
+
+    CursorStreamWrapper(InputStream& is):
+        GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
+
+    // counting line and column number
+    Ch Take() {
+        Ch ch = this->is_.Take();
+        if(ch == '\n') {
+            line_ ++;
+            col_ = 0;
+        } else {
+            col_ ++;
+        }
+        return ch;
+    }
+
+    //! Get the error line number, if error exists.
+    size_t GetLine() const { return line_; }
+    //! Get the error column number, if error exists.
+    size_t GetColumn() const { return col_; }
+
+private:
+    size_t line_;   //!< Current Line
+    size_t col_;    //!< Current Column
+};
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+RAPIDJSON_DIAG_POP
+#endif
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_
diff --git a/src/lottie/rapidjson/document.h b/src/lottie/rapidjson/document.h
new file mode 100644 (file)
index 0000000..aeca757
--- /dev/null
@@ -0,0 +1,2672 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_DOCUMENT_H_
+#define RAPIDJSON_DOCUMENT_H_
+
+/*! \file document.h */
+
+#include "reader.h"
+#include "internal/meta.h"
+#include "internal/strfunc.h"
+#include "memorystream.h"
+#include "encodedstream.h"
+#include <new>      // placement new
+#include <limits>
+
+RAPIDJSON_DIAG_PUSH
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_OFF(effc++)
+#if __GNUC__ >= 6
+RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions
+#endif
+#endif // __GNUC__
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+#include <iterator> // std::iterator, std::random_access_iterator_tag
+#endif
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// Forward declaration.
+template <typename Encoding, typename Allocator>
+class GenericValue;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+//! Name-value pair in a JSON object value.
+/*!
+    This class was internal to GenericValue. It used to be a inner struct.
+    But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct.
+    https://code.google.com/p/rapidjson/issues/detail?id=64
+*/
+template <typename Encoding, typename Allocator> 
+struct GenericMember { 
+    GenericValue<Encoding, Allocator> name;     //!< name of member (must be a string)
+    GenericValue<Encoding, Allocator> value;    //!< value of member.
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericMemberIterator
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+
+//! (Constant) member iterator for a JSON object value
+/*!
+    \tparam Const Is this a constant iterator?
+    \tparam Encoding    Encoding of the value. (Even non-string values need to have the same encoding in a document)
+    \tparam Allocator   Allocator type for allocating memory of object, array and string.
+
+    This class implements a Random Access Iterator for GenericMember elements
+    of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements].
+
+    \note This iterator implementation is mainly intended to avoid implicit
+        conversions from iterator values to \c NULL,
+        e.g. from GenericValue::FindMember.
+
+    \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a
+        pointer-based implementation, if your platform doesn't provide
+        the C++ <iterator> header.
+
+    \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
+ */
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator
+    : public std::iterator<std::random_access_iterator_tag
+        , typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
+
+    friend class GenericValue<Encoding,Allocator>;
+    template <bool, typename, typename> friend class GenericMemberIterator;
+
+    typedef GenericMember<Encoding,Allocator> PlainType;
+    typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+    typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
+
+public:
+    //! Iterator type itself
+    typedef GenericMemberIterator Iterator;
+    //! Constant iterator type
+    typedef GenericMemberIterator<true,Encoding,Allocator>  ConstIterator;
+    //! Non-constant iterator type
+    typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator;
+
+    //! Pointer to (const) GenericMember
+    typedef typename BaseType::pointer         Pointer;
+    //! Reference to (const) GenericMember
+    typedef typename BaseType::reference       Reference;
+    //! Signed integer type (e.g. \c ptrdiff_t)
+    typedef typename BaseType::difference_type DifferenceType;
+
+    //! Default constructor (singular value)
+    /*! Creates an iterator pointing to no element.
+        \note All operations, except for comparisons, are undefined on such values.
+     */
+    GenericMemberIterator() : ptr_() {}
+
+    //! Iterator conversions to more const
+    /*!
+        \param it (Non-const) iterator to copy from
+
+        Allows the creation of an iterator from another GenericMemberIterator
+        that is "less const".  Especially, creating a non-constant iterator
+        from a constant iterator are disabled:
+        \li const -> non-const (not ok)
+        \li const -> const (ok)
+        \li non-const -> const (ok)
+        \li non-const -> non-const (ok)
+
+        \note If the \c Const template parameter is already \c false, this
+            constructor effectively defines a regular copy-constructor.
+            Otherwise, the copy constructor is implicitly defined.
+    */
+    GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {}
+    Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; }
+
+    //! @name stepping
+    //@{
+    Iterator& operator++(){ ++ptr_; return *this; }
+    Iterator& operator--(){ --ptr_; return *this; }
+    Iterator  operator++(int){ Iterator old(*this); ++ptr_; return old; }
+    Iterator  operator--(int){ Iterator old(*this); --ptr_; return old; }
+    //@}
+
+    //! @name increment/decrement
+    //@{
+    Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); }
+    Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); }
+
+    Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; }
+    Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; }
+    //@}
+
+    //! @name relations
+    //@{
+    bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; }
+    bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; }
+    bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; }
+    bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; }
+    bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; }
+    bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; }
+    //@}
+
+    //! @name dereference
+    //@{
+    Reference operator*() const { return *ptr_; }
+    Pointer   operator->() const { return ptr_; }
+    Reference operator[](DifferenceType n) const { return ptr_[n]; }
+    //@}
+
+    //! Distance
+    DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; }
+
+private:
+    //! Internal constructor from plain pointer
+    explicit GenericMemberIterator(Pointer p) : ptr_(p) {}
+
+    Pointer ptr_; //!< raw pointer
+};
+
+#else // RAPIDJSON_NOMEMBERITERATORCLASS
+
+// class-based member iterator implementation disabled, use plain pointers
+
+template <bool Const, typename Encoding, typename Allocator>
+struct GenericMemberIterator;
+
+//! non-const GenericMemberIterator
+template <typename Encoding, typename Allocator>
+struct GenericMemberIterator<false,Encoding,Allocator> {
+    //! use plain pointer as iterator type
+    typedef GenericMember<Encoding,Allocator>* Iterator;
+};
+//! const GenericMemberIterator
+template <typename Encoding, typename Allocator>
+struct GenericMemberIterator<true,Encoding,Allocator> {
+    //! use plain const pointer as iterator type
+    typedef const GenericMember<Encoding,Allocator>* Iterator;
+};
+
+#endif // RAPIDJSON_NOMEMBERITERATORCLASS
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericStringRef
+
+//! Reference to a constant string (not taking a copy)
+/*!
+    \tparam CharType character type of the string
+
+    This helper class is used to automatically infer constant string
+    references for string literals, especially from \c const \b (!)
+    character arrays.
+
+    The main use is for creating JSON string values without copying the
+    source string via an \ref Allocator.  This requires that the referenced
+    string pointers have a sufficient lifetime, which exceeds the lifetime
+    of the associated GenericValue.
+
+    \b Example
+    \code
+    Value v("foo");   // ok, no need to copy & calculate length
+    const char foo[] = "foo";
+    v.SetString(foo); // ok
+
+    const char* bar = foo;
+    // Value x(bar); // not ok, can't rely on bar's lifetime
+    Value x(StringRef(bar)); // lifetime explicitly guaranteed by user
+    Value y(StringRef(bar, 3));  // ok, explicitly pass length
+    \endcode
+
+    \see StringRef, GenericValue::SetString
+*/
+template<typename CharType>
+struct GenericStringRef {
+    typedef CharType Ch; //!< character type of the string
+
+    //! Create string reference from \c const character array
+#ifndef __clang__ // -Wdocumentation
+    /*!
+        This constructor implicitly creates a constant string reference from
+        a \c const character array.  It has better performance than
+        \ref StringRef(const CharType*) by inferring the string \ref length
+        from the array length, and also supports strings containing null
+        characters.
+
+        \tparam N length of the string, automatically inferred
+
+        \param str Constant character array, lifetime assumed to be longer
+            than the use of the string in e.g. a GenericValue
+
+        \post \ref s == str
+
+        \note Constant complexity.
+        \note There is a hidden, private overload to disallow references to
+            non-const character arrays to be created via this constructor.
+            By this, e.g. function-scope arrays used to be filled via
+            \c snprintf are excluded from consideration.
+            In such cases, the referenced string should be \b copied to the
+            GenericValue instead.
+     */
+#endif
+    template<SizeType N>
+    GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT
+        : s(str), length(N-1) {}
+
+    //! Explicitly create string reference from \c const character pointer
+#ifndef __clang__ // -Wdocumentation
+    /*!
+        This constructor can be used to \b explicitly  create a reference to
+        a constant string pointer.
+
+        \see StringRef(const CharType*)
+
+        \param str Constant character pointer, lifetime assumed to be longer
+            than the use of the string in e.g. a GenericValue
+
+        \post \ref s == str
+
+        \note There is a hidden, private overload to disallow references to
+            non-const character arrays to be created via this constructor.
+            By this, e.g. function-scope arrays used to be filled via
+            \c snprintf are excluded from consideration.
+            In such cases, the referenced string should be \b copied to the
+            GenericValue instead.
+     */
+#endif
+    explicit GenericStringRef(const CharType* str)
+        : s(str), length(NotNullStrLen(str)) {}
+
+    //! Create constant string reference from pointer and length
+#ifndef __clang__ // -Wdocumentation
+    /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+        \param len length of the string, excluding the trailing NULL terminator
+
+        \post \ref s == str && \ref length == len
+        \note Constant complexity.
+     */
+#endif
+    GenericStringRef(const CharType* str, SizeType len)
+        : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); }
+
+    GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {}
+
+    //! implicit conversion to plain CharType pointer
+    operator const Ch *() const { return s; }
+
+    const Ch* const s; //!< plain CharType pointer
+    const SizeType length; //!< length of the string (excluding the trailing NULL terminator)
+
+private:
+    SizeType NotNullStrLen(const CharType* str) {
+        RAPIDJSON_ASSERT(str != 0);
+        return internal::StrLen(str);
+    }
+
+    /// Empty string - used when passing in a NULL pointer
+    static const Ch emptyString[];
+
+    //! Disallow construction from non-const array
+    template<SizeType N>
+    GenericStringRef(CharType (&str)[N]) /* = delete */;
+    //! Copy assignment operator not permitted - immutable type
+    GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */;
+};
+
+template<typename CharType>
+const CharType GenericStringRef<CharType>::emptyString[] = { CharType() };
+
+//! Mark a character pointer as constant string
+/*! Mark a plain character pointer as a "string literal".  This function
+    can be used to avoid copying a character string to be referenced as a
+    value in a JSON GenericValue object, if the string's lifetime is known
+    to be valid long enough.
+    \tparam CharType Character type of the string
+    \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+    \return GenericStringRef string reference object
+    \relatesalso GenericStringRef
+
+    \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const CharType* str) {
+    return GenericStringRef<CharType>(str);
+}
+
+//! Mark a character pointer as constant string
+/*! Mark a plain character pointer as a "string literal".  This function
+    can be used to avoid copying a character string to be referenced as a
+    value in a JSON GenericValue object, if the string's lifetime is known
+    to be valid long enough.
+
+    This version has better performance with supplied length, and also
+    supports string containing null characters.
+
+    \tparam CharType character type of the string
+    \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+    \param length The length of source string.
+    \return GenericStringRef string reference object
+    \relatesalso GenericStringRef
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) {
+    return GenericStringRef<CharType>(str, SizeType(length));
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+//! Mark a string object as constant string
+/*! Mark a string object (e.g. \c std::string) as a "string literal".
+    This function can be used to avoid copying a string to be referenced as a
+    value in a JSON GenericValue object, if the string's lifetime is known
+    to be valid long enough.
+
+    \tparam CharType character type of the string
+    \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+    \return GenericStringRef string reference object
+    \relatesalso GenericStringRef
+    \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const std::basic_string<CharType>& str) {
+    return GenericStringRef<CharType>(str.data(), SizeType(str.size()));
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericValue type traits
+namespace internal {
+
+template <typename T, typename Encoding = void, typename Allocator = void>
+struct IsGenericValueImpl : FalseType {};
+
+// select candidates according to nested encoding and allocator types
+template <typename T> struct IsGenericValueImpl<T, typename Void<typename T::EncodingType>::Type, typename Void<typename T::AllocatorType>::Type>
+    : IsBaseOf<GenericValue<typename T::EncodingType, typename T::AllocatorType>, T>::Type {};
+
+// helper to match arbitrary GenericValue instantiations, including derived classes
+template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// TypeHelper
+
+namespace internal {
+
+template <typename ValueType, typename T>
+struct TypeHelper {};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, bool> {
+    static bool Is(const ValueType& v) { return v.IsBool(); }
+    static bool Get(const ValueType& v) { return v.GetBool(); }
+    static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); }
+    static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, int> {
+    static bool Is(const ValueType& v) { return v.IsInt(); }
+    static int Get(const ValueType& v) { return v.GetInt(); }
+    static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); }
+    static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, unsigned> {
+    static bool Is(const ValueType& v) { return v.IsUint(); }
+    static unsigned Get(const ValueType& v) { return v.GetUint(); }
+    static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); }
+    static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, int64_t> {
+    static bool Is(const ValueType& v) { return v.IsInt64(); }
+    static int64_t Get(const ValueType& v) { return v.GetInt64(); }
+    static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); }
+    static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, uint64_t> {
+    static bool Is(const ValueType& v) { return v.IsUint64(); }
+    static uint64_t Get(const ValueType& v) { return v.GetUint64(); }
+    static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); }
+    static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, double> {
+    static bool Is(const ValueType& v) { return v.IsDouble(); }
+    static double Get(const ValueType& v) { return v.GetDouble(); }
+    static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); }
+    static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, float> {
+    static bool Is(const ValueType& v) { return v.IsFloat(); }
+    static float Get(const ValueType& v) { return v.GetFloat(); }
+    static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); }
+    static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, const typename ValueType::Ch*> {
+    typedef const typename ValueType::Ch* StringType;
+    static bool Is(const ValueType& v) { return v.IsString(); }
+    static StringType Get(const ValueType& v) { return v.GetString(); }
+    static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); }
+    static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+
+#if RAPIDJSON_HAS_STDSTRING
+template<typename ValueType> 
+struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > {
+    typedef std::basic_string<typename ValueType::Ch> StringType;
+    static bool Is(const ValueType& v) { return v.IsString(); }
+    static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); }
+    static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+#endif
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, typename ValueType::Array> {
+    typedef typename ValueType::Array ArrayType;
+    static bool Is(const ValueType& v) { return v.IsArray(); }
+    static ArrayType Get(ValueType& v) { return v.GetArray(); }
+    static ValueType& Set(ValueType& v, ArrayType data) { return v = data; }
+    static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, typename ValueType::ConstArray> {
+    typedef typename ValueType::ConstArray ArrayType;
+    static bool Is(const ValueType& v) { return v.IsArray(); }
+    static ArrayType Get(const ValueType& v) { return v.GetArray(); }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, typename ValueType::Object> {
+    typedef typename ValueType::Object ObjectType;
+    static bool Is(const ValueType& v) { return v.IsObject(); }
+    static ObjectType Get(ValueType& v) { return v.GetObject(); }
+    static ValueType& Set(ValueType& v, ObjectType data) { return v = data; }
+    static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; }
+};
+
+template<typename ValueType> 
+struct TypeHelper<ValueType, typename ValueType::ConstObject> {
+    typedef typename ValueType::ConstObject ObjectType;
+    static bool Is(const ValueType& v) { return v.IsObject(); }
+    static ObjectType Get(const ValueType& v) { return v.GetObject(); }
+};
+
+} // namespace internal
+
+// Forward declarations
+template <bool, typename> class GenericArray;
+template <bool, typename> class GenericObject;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericValue
+
+//! Represents a JSON value. Use Value for UTF8 encoding and default allocator.
+/*!
+    A JSON value can be one of 7 types. This class is a variant type supporting
+    these types.
+
+    Use the Value if UTF8 and default allocator
+
+    \tparam Encoding    Encoding of the value. (Even non-string values need to have the same encoding in a document)
+    \tparam Allocator   Allocator type for allocating memory of object, array and string.
+*/
+template <typename Encoding, typename Allocator = MemoryPoolAllocator<> > 
+class GenericValue {
+public:
+    //! Name-value pair in an object.
+    typedef GenericMember<Encoding, Allocator> Member;
+    typedef Encoding EncodingType;                  //!< Encoding type from template parameter.
+    typedef Allocator AllocatorType;                //!< Allocator type from template parameter.
+    typedef typename Encoding::Ch Ch;               //!< Character type derived from Encoding.
+    typedef GenericStringRef<Ch> StringRefType;     //!< Reference to a constant string
+    typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator;  //!< Member iterator for iterating in object.
+    typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator;  //!< Constant member iterator for iterating in object.
+    typedef GenericValue* ValueIterator;            //!< Value iterator for iterating in array.
+    typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
+    typedef GenericValue<Encoding, Allocator> ValueType;    //!< Value type of itself.
+    typedef GenericArray<false, ValueType> Array;
+    typedef GenericArray<true, ValueType> ConstArray;
+    typedef GenericObject<false, ValueType> Object;
+    typedef GenericObject<true, ValueType> ConstObject;
+
+    //!@name Constructors and destructor.
+    //@{
+
+    //! Default constructor creates a null value.
+    GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Move constructor in C++11
+    GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) {
+        rhs.data_.f.flags = kNullFlag; // give up contents
+    }
+#endif
+
+private:
+    //! Copy constructor is not permitted.
+    GenericValue(const GenericValue& rhs);
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Moving from a GenericDocument is not permitted.
+    template <typename StackAllocator>
+    GenericValue(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs);
+
+    //! Move assignment from a GenericDocument is not permitted.
+    template <typename StackAllocator>
+    GenericValue& operator=(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs);
+#endif
+
+public:
+
+    //! Constructor with JSON value type.
+    /*! This creates a Value of specified type with default content.
+        \param type Type of the value.
+        \note Default content for number is zero.
+    */
+    explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
+        static const uint16_t defaultFlags[7] = {
+            kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
+            kNumberAnyFlag
+        };
+        RAPIDJSON_ASSERT(type >= kNullType && type <= kNumberType);
+        data_.f.flags = defaultFlags[type];
+
+        // Use ShortString to store empty string.
+        if (type == kStringType)
+            data_.ss.SetLength(0);
+    }
+
+    //! Explicit copy constructor (with allocator)
+    /*! Creates a copy of a Value by using the given Allocator
+        \tparam SourceAllocator allocator of \c rhs
+        \param rhs Value to copy from (read-only)
+        \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator().
+        \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer)
+        \see CopyFrom()
+    */
+    template <typename SourceAllocator>
+    GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings = false) {
+        switch (rhs.GetType()) {
+        case kObjectType: {
+                SizeType count = rhs.data_.o.size;
+                Member* lm = reinterpret_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
+                const typename GenericValue<Encoding,SourceAllocator>::Member* rm = rhs.GetMembersPointer();
+                for (SizeType i = 0; i < count; i++) {
+                    new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings);
+                    new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings);
+                }
+                data_.f.flags = kObjectFlag;
+                data_.o.size = data_.o.capacity = count;
+                SetMembersPointer(lm);
+            }
+            break;
+        case kArrayType: {
+                SizeType count = rhs.data_.a.size;
+                GenericValue* le = reinterpret_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
+                const GenericValue<Encoding,SourceAllocator>* re = rhs.GetElementsPointer();
+                for (SizeType i = 0; i < count; i++)
+                    new (&le[i]) GenericValue(re[i], allocator, copyConstStrings);
+                data_.f.flags = kArrayFlag;
+                data_.a.size = data_.a.capacity = count;
+                SetElementsPointer(le);
+            }
+            break;
+        case kStringType:
+            if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) {
+                data_.f.flags = rhs.data_.f.flags;
+                data_  = *reinterpret_cast<const Data*>(&rhs.data_);
+            }
+            else
+                SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
+            break;
+        default:
+            data_.f.flags = rhs.data_.f.flags;
+            data_  = *reinterpret_cast<const Data*>(&rhs.data_);
+            break;
+        }
+    }
+
+    //! Constructor for boolean value.
+    /*! \param b Boolean value
+        \note This constructor is limited to \em real boolean values and rejects
+            implicitly converted types like arbitrary pointers.  Use an explicit cast
+            to \c bool, if you want to construct a boolean JSON value in such cases.
+     */
+#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
+    template <typename T>
+    explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<bool, T>))) RAPIDJSON_NOEXCEPT  // See #472
+#else
+    explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
+#endif
+        : data_() {
+            // safe-guard against failing SFINAE
+            RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value));
+            data_.f.flags = b ? kTrueFlag : kFalseFlag;
+    }
+
+    //! Constructor for int value.
+    explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
+        data_.n.i64 = i;
+        data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag;
+    }
+
+    //! Constructor for unsigned value.
+    explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
+        data_.n.u64 = u; 
+        data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag);
+    }
+
+    //! Constructor for int64_t value.
+    explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() {
+        data_.n.i64 = i64;
+        data_.f.flags = kNumberInt64Flag;
+        if (i64 >= 0) {
+            data_.f.flags |= kNumberUint64Flag;
+            if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
+                data_.f.flags |= kUintFlag;
+            if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+                data_.f.flags |= kIntFlag;
+        }
+        else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+            data_.f.flags |= kIntFlag;
+    }
+
+    //! Constructor for uint64_t value.
+    explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() {
+        data_.n.u64 = u64;
+        data_.f.flags = kNumberUint64Flag;
+        if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
+            data_.f.flags |= kInt64Flag;
+        if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
+            data_.f.flags |= kUintFlag;
+        if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+            data_.f.flags |= kIntFlag;
+    }
+
+    //! Constructor for double value.
+    explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
+
+    //! Constructor for float value.
+    explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast<double>(f); data_.f.flags = kNumberDoubleFlag; }
+
+    //! Constructor for constant string (i.e. do not make a copy of string)
+    GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
+
+    //! Constructor for constant string (i.e. do not make a copy of string)
+    explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); }
+
+    //! Constructor for copy-string (i.e. do make a copy of string)
+    GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); }
+
+    //! Constructor for copy-string (i.e. do make a copy of string)
+    GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Constructor for copy-string from a string object (i.e. do make a copy of string)
+    /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+     */
+    GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
+#endif
+
+    //! Constructor for Array.
+    /*!
+        \param a An array obtained by \c GetArray().
+        \note \c Array is always pass-by-value.
+        \note the source array is moved into this value and the sourec array becomes empty.
+    */
+    GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) {
+        a.value_.data_ = Data();
+        a.value_.data_.f.flags = kArrayFlag;
+    }
+
+    //! Constructor for Object.
+    /*!
+        \param o An object obtained by \c GetObject().
+        \note \c Object is always pass-by-value.
+        \note the source object is moved into this value and the sourec object becomes empty.
+    */
+    GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) {
+        o.value_.data_ = Data();
+        o.value_.data_.f.flags = kObjectFlag;
+    }
+
+    //! Destructor.
+    /*! Need to destruct elements of array, members of object, or copy-string.
+    */
+    ~GenericValue() {
+        if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+            switch(data_.f.flags) {
+            case kArrayFlag:
+                {
+                    GenericValue* e = GetElementsPointer();
+                    for (GenericValue* v = e; v != e + data_.a.size; ++v)
+                        v->~GenericValue();
+                    Allocator::Free(e);
+                }
+                break;
+
+            case kObjectFlag:
+                for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+                    m->~Member();
+                Allocator::Free(GetMembersPointer());
+                break;
+
+            case kCopyStringFlag:
+                Allocator::Free(const_cast<Ch*>(GetStringPointer()));
+                break;
+
+            default:
+                break;  // Do nothing for other types.
+            }
+        }
+    }
+
+    //@}
+
+    //!@name Assignment operators
+    //@{
+
+    //! Assignment with move semantics.
+    /*! \param rhs Source of the assignment. It will become a null value after assignment.
+    */
+    GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
+        RAPIDJSON_ASSERT(this != &rhs);
+        this->~GenericValue();
+        RawAssign(rhs);
+        return *this;
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Move assignment in C++11
+    GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT {
+        return *this = rhs.Move();
+    }
+#endif
+
+    //! Assignment of constant string reference (no copy)
+    /*! \param str Constant string reference to be assigned
+        \note This overload is needed to avoid clashes with the generic primitive type assignment overload below.
+        \see GenericStringRef, operator=(T)
+    */
+    GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT {
+        GenericValue s(str);
+        return *this = s;
+    }
+
+    //! Assignment with primitive types.
+    /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+        \param value The value to be assigned.
+
+        \note The source type \c T explicitly disallows all pointer types,
+            especially (\c const) \ref Ch*.  This helps avoiding implicitly
+            referencing character strings with insufficient lifetime, use
+            \ref SetString(const Ch*, Allocator&) (for copying) or
+            \ref StringRef() (to explicitly mark the pointer as constant) instead.
+            All other pointer types would implicitly convert to \c bool,
+            use \ref SetBool() instead.
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer<T>), (GenericValue&))
+    operator=(T value) {
+        GenericValue v(value);
+        return *this = v;
+    }
+
+    //! Deep-copy assignment from Value
+    /*! Assigns a \b copy of the Value to the current Value object
+        \tparam SourceAllocator Allocator type of \c rhs
+        \param rhs Value to copy from (read-only)
+        \param allocator Allocator to use for copying
+        \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer)
+     */
+    template <typename SourceAllocator>
+    GenericValue& CopyFrom(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings = false) {
+        RAPIDJSON_ASSERT(static_cast<void*>(this) != static_cast<void const*>(&rhs));
+        this->~GenericValue();
+        new (this) GenericValue(rhs, allocator, copyConstStrings);
+        return *this;
+    }
+
+    //! Exchange the contents of this value with those of other.
+    /*!
+        \param other Another value.
+        \note Constant complexity.
+    */
+    GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT {
+        GenericValue temp;
+        temp.RawAssign(*this);
+        RawAssign(other);
+        other.RawAssign(temp);
+        return *this;
+    }
+
+    //! free-standing swap function helper
+    /*!
+        Helper function to enable support for common swap implementation pattern based on \c std::swap:
+        \code
+        void swap(MyClass& a, MyClass& b) {
+            using std::swap;
+            swap(a.value, b.value);
+            // ...
+        }
+        \endcode
+        \see Swap()
+     */
+    friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+
+    //! Prepare Value for move semantics
+    /*! \return *this */
+    GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; }
+    //@}
+
+    //!@name Equal-to and not-equal-to operators
+    //@{
+    //! Equal-to operator
+    /*!
+        \note If an object contains duplicated named member, comparing equality with any object is always \c false.
+        \note Linear time complexity (number of all values in the subtree and total lengths of all strings).
+    */
+    template <typename SourceAllocator>
+    bool operator==(const GenericValue<Encoding, SourceAllocator>& rhs) const {
+        typedef GenericValue<Encoding, SourceAllocator> RhsType;
+        if (GetType() != rhs.GetType())
+            return false;
+
+        switch (GetType()) {
+        case kObjectType: // Warning: O(n^2) inner-loop
+            if (data_.o.size != rhs.data_.o.size)
+                return false;           
+            for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) {
+                typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name);
+                if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value)
+                    return false;
+            }
+            return true;
+            
+        case kArrayType:
+            if (data_.a.size != rhs.data_.a.size)
+                return false;
+            for (SizeType i = 0; i < data_.a.size; i++)
+                if ((*this)[i] != rhs[i])
+                    return false;
+            return true;
+
+        case kStringType:
+            return StringEqual(rhs);
+
+        case kNumberType:
+            if (IsDouble() || rhs.IsDouble()) {
+                double a = GetDouble();     // May convert from integer to double.
+                double b = rhs.GetDouble(); // Ditto
+                return a >= b && a <= b;    // Prevent -Wfloat-equal
+            }
+            else
+                return data_.n.u64 == rhs.data_.n.u64;
+
+        default:
+            return true;
+        }
+    }
+
+    //! Equal-to operator with const C-string pointer
+    bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Equal-to operator with string object
+    /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+     */
+    bool operator==(const std::basic_string<Ch>& rhs) const { return *this == GenericValue(StringRef(rhs)); }
+#endif
+
+    //! Equal-to operator with primitive types
+    /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false
+    */
+    template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>,internal::IsGenericValue<T> >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); }
+
+    //! Not-equal-to operator
+    /*! \return !(*this == rhs)
+     */
+    template <typename SourceAllocator>
+    bool operator!=(const GenericValue<Encoding, SourceAllocator>& rhs) const { return !(*this == rhs); }
+
+    //! Not-equal-to operator with const C-string pointer
+    bool operator!=(const Ch* rhs) const { return !(*this == rhs); }
+
+    //! Not-equal-to operator with arbitrary types
+    /*! \return !(*this == rhs)
+     */
+    template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); }
+
+    //! Equal-to operator with arbitrary types (symmetric version)
+    /*! \return (rhs == lhs)
+     */
+    template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; }
+
+    //! Not-Equal-to operator with arbitrary types (symmetric version)
+    /*! \return !(rhs == lhs)
+     */
+    template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); }
+    //@}
+
+    //!@name Type
+    //@{
+
+    Type GetType()  const { return static_cast<Type>(data_.f.flags & kTypeMask); }
+    bool IsNull()   const { return data_.f.flags == kNullFlag; }
+    bool IsFalse()  const { return data_.f.flags == kFalseFlag; }
+    bool IsTrue()   const { return data_.f.flags == kTrueFlag; }
+    bool IsBool()   const { return (data_.f.flags & kBoolFlag) != 0; }
+    bool IsObject() const { return data_.f.flags == kObjectFlag; }
+    bool IsArray()  const { return data_.f.flags == kArrayFlag; }
+    bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
+    bool IsInt()    const { return (data_.f.flags & kIntFlag) != 0; }
+    bool IsUint()   const { return (data_.f.flags & kUintFlag) != 0; }
+    bool IsInt64()  const { return (data_.f.flags & kInt64Flag) != 0; }
+    bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
+    bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
+    bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
+
+    // Checks whether a number can be losslessly converted to a double.
+    bool IsLosslessDouble() const {
+        if (!IsNumber()) return false;
+        if (IsUint64()) {
+            uint64_t u = GetUint64();
+            volatile double d = static_cast<double>(u);
+            return (d >= 0.0)
+                && (d < static_cast<double>((std::numeric_limits<uint64_t>::max)()))
+                && (u == static_cast<uint64_t>(d));
+        }
+        if (IsInt64()) {
+            int64_t i = GetInt64();
+            volatile double d = static_cast<double>(i);
+            return (d >= static_cast<double>((std::numeric_limits<int64_t>::min)()))
+                && (d < static_cast<double>((std::numeric_limits<int64_t>::max)()))
+                && (i == static_cast<int64_t>(d));
+        }
+        return true; // double, int, uint are always lossless
+    }
+
+    // Checks whether a number is a float (possible lossy).
+    bool IsFloat() const  {
+        if ((data_.f.flags & kDoubleFlag) == 0)
+            return false;
+        double d = GetDouble();
+        return d >= -3.4028234e38 && d <= 3.4028234e38;
+    }
+    // Checks whether a number can be losslessly converted to a float.
+    bool IsLosslessFloat() const {
+        if (!IsNumber()) return false;
+        double a = GetDouble();
+        if (a < static_cast<double>(-(std::numeric_limits<float>::max)())
+                || a > static_cast<double>((std::numeric_limits<float>::max)()))
+            return false;
+        double b = static_cast<double>(static_cast<float>(a));
+        return a >= b && a <= b;    // Prevent -Wfloat-equal
+    }
+
+    //@}
+
+    //!@name Null
+    //@{
+
+    GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; }
+
+    //@}
+
+    //!@name Bool
+    //@{
+
+    bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; }
+    //!< Set boolean value
+    /*! \post IsBool() == true */
+    GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
+
+    //@}
+
+    //!@name Object
+    //@{
+
+    //! Set this value as an empty object.
+    /*! \post IsObject() == true */
+    GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; }
+
+    //! Get the number of members in the object.
+    SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; }
+
+    //! Get the capacity of object.
+    SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; }
+
+    //! Check whether the object is empty.
+    bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
+
+    //! Get a value from an object associated with the name.
+    /*! \pre IsObject() == true
+        \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType))
+        \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7.
+        Since 0.2, if the name is not correct, it will assert.
+        If user is unsure whether a member exists, user should use HasMember() first.
+        A better approach is to use FindMember().
+        \note Linear time complexity.
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(GenericValue&)) operator[](T* name) {
+        GenericValue n(StringRef(name));
+        return (*this)[n];
+    }
+
+    template <typename T>
+    float optFloat(T* name, float fallback = 0.0) {
+        GenericValue n(StringRef(name));
+        return optFloat(n, fallback);
+    }
+
+    template <typename T>
+    double optDouble(T* name, double fallback = 0.0) {
+        GenericValue n(StringRef(name));
+        return optDouble(n, fallback);
+    }
+
+    template <typename T>
+    int optInt(T* name, int fallback=0) {
+        GenericValue n(StringRef(name));
+        return optInt(n, fallback);
+    }
+
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast<GenericValue&>(*this)[name]; }
+
+    //! Get a value from an object associated with the name.
+    /*! \pre IsObject() == true
+        \tparam SourceAllocator Allocator of the \c name value
+
+        \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen().
+        And it can also handle strings with embedded null characters.
+
+        \note Linear time complexity.
+    */
+    template <typename SourceAllocator>
+    GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) {
+        MemberIterator member = FindMember(name);
+        if (member != MemberEnd())
+            return member->value;
+        else {
+            RAPIDJSON_ASSERT(false);    // see above note
+
+            // This will generate -Wexit-time-destructors in clang
+            // static GenericValue NullValue;
+            // return NullValue;
+
+            // Use static buffer and placement-new to prevent destruction
+            static char buffer[sizeof(GenericValue)];
+            return *new (buffer) GenericValue();
+        }
+    }
+
+    template <typename SourceAllocator>
+    float optFloat(const GenericValue<Encoding, SourceAllocator>& name, float fallback) {
+        MemberIterator member = FindMember(name);
+        if (member != MemberEnd())
+            return member->value.GetFloat();
+        return fallback;
+    }
+
+    template <typename SourceAllocator>
+    double optDouble(const GenericValue<Encoding, SourceAllocator>& name, double fallback) {
+        MemberIterator member = FindMember(name);
+        if (member != MemberEnd())
+            return member->value.GetDouble();
+        return fallback;
+    }
+
+    template <typename SourceAllocator>
+    int optInt(const GenericValue<Encoding, SourceAllocator>& name, int fallback) {
+        MemberIterator member = FindMember(name);
+        if (member != MemberEnd() && member->value.IsInt())
+            return member->value.GetInt();
+        return fallback;
+    }
+
+    template <typename SourceAllocator>
+    const GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this)[name]; }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Get a value from an object associated with name (string object).
+    GenericValue& operator[](const std::basic_string<Ch>& name) { return (*this)[GenericValue(StringRef(name))]; }
+    const GenericValue& operator[](const std::basic_string<Ch>& name) const { return (*this)[GenericValue(StringRef(name))]; }
+#endif
+
+    //! Const member iterator
+    /*! \pre IsObject() == true */
+    ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); }
+    //! Const \em past-the-end member iterator
+    /*! \pre IsObject() == true */
+    ConstMemberIterator MemberEnd() const   { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); }
+    //! Member iterator
+    /*! \pre IsObject() == true */
+    MemberIterator MemberBegin()            { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); }
+    //! \em Past-the-end member iterator
+    /*! \pre IsObject() == true */
+    MemberIterator MemberEnd()              { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
+
+    //! Request the object to have enough capacity to store members.
+    /*! \param newCapacity  The capacity that the object at least need to have.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \note Linear time complexity.
+    */
+    GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) {
+        RAPIDJSON_ASSERT(IsObject());
+        if (newCapacity > data_.o.capacity) {
+            SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member))));
+            data_.o.capacity = newCapacity;
+        }
+        return *this;
+    }
+
+    //! Check whether a member exists in the object.
+    /*!
+        \param name Member name to be searched.
+        \pre IsObject() == true
+        \return Whether a member with that name exists.
+        \note It is better to use FindMember() directly if you need the obtain the value as well.
+        \note Linear time complexity.
+    */
+    bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Check whether a member exists in the object with string object.
+    /*!
+        \param name Member name to be searched.
+        \pre IsObject() == true
+        \return Whether a member with that name exists.
+        \note It is better to use FindMember() directly if you need the obtain the value as well.
+        \note Linear time complexity.
+    */
+    bool HasMember(const std::basic_string<Ch>& name) const { return FindMember(name) != MemberEnd(); }
+#endif
+
+    //! Check whether a member exists in the object with GenericValue name.
+    /*!
+        This version is faster because it does not need a StrLen(). It can also handle string with null character.
+        \param name Member name to be searched.
+        \pre IsObject() == true
+        \return Whether a member with that name exists.
+        \note It is better to use FindMember() directly if you need the obtain the value as well.
+        \note Linear time complexity.
+    */
+    template <typename SourceAllocator>
+    bool HasMember(const GenericValue<Encoding, SourceAllocator>& name) const { return FindMember(name) != MemberEnd(); }
+
+    //! Find member by name.
+    /*!
+        \param name Member name to be searched.
+        \pre IsObject() == true
+        \return Iterator to member, if it exists.
+            Otherwise returns \ref MemberEnd().
+
+        \note Earlier versions of Rapidjson returned a \c NULL pointer, in case
+            the requested member doesn't exist. For consistency with e.g.
+            \c std::map, this has been changed to MemberEnd() now.
+        \note Linear time complexity.
+    */
+    MemberIterator FindMember(const Ch* name) {
+        GenericValue n(StringRef(name));
+        return FindMember(n);
+    }
+
+    ConstMemberIterator FindMember(const Ch* name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
+
+    //! Find member by name.
+    /*!
+        This version is faster because it does not need a StrLen(). It can also handle string with null character.
+        \param name Member name to be searched.
+        \pre IsObject() == true
+        \return Iterator to member, if it exists.
+            Otherwise returns \ref MemberEnd().
+
+        \note Earlier versions of Rapidjson returned a \c NULL pointer, in case
+            the requested member doesn't exist. For consistency with e.g.
+            \c std::map, this has been changed to MemberEnd() now.
+        \note Linear time complexity.
+    */
+    template <typename SourceAllocator>
+    MemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) {
+        RAPIDJSON_ASSERT(IsObject());
+        RAPIDJSON_ASSERT(name.IsString());
+        MemberIterator member = MemberBegin();
+        for ( ; member != MemberEnd(); ++member)
+            if (name.StringEqual(member->name))
+                break;
+        return member;
+    }
+    template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Find member by string object name.
+    /*!
+        \param name Member name to be searched.
+        \pre IsObject() == true
+        \return Iterator to member, if it exists.
+            Otherwise returns \ref MemberEnd().
+    */
+    MemberIterator FindMember(const std::basic_string<Ch>& name) { return FindMember(GenericValue(StringRef(name))); }
+    ConstMemberIterator FindMember(const std::basic_string<Ch>& name) const { return FindMember(GenericValue(StringRef(name))); }
+#endif
+
+    //! Add a member (name-value pair) to the object.
+    /*! \param name A string value as name of member.
+        \param value Value of any type.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \note The ownership of \c name and \c value will be transferred to this object on success.
+        \pre  IsObject() && name.IsString()
+        \post name.IsNull() && value.IsNull()
+        \note Amortized Constant time complexity.
+    */
+    GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
+        RAPIDJSON_ASSERT(IsObject());
+        RAPIDJSON_ASSERT(name.IsString());
+
+        ObjectData& o = data_.o;
+        if (o.size >= o.capacity)
+            MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator);
+        Member* members = GetMembersPointer();
+        members[o.size].name.RawAssign(name);
+        members[o.size].value.RawAssign(value);
+        o.size++;
+        return *this;
+    }
+
+    //! Add a constant string value as member (name-value pair) to the object.
+    /*! \param name A string value as name of member.
+        \param value constant string reference as value of member.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \pre  IsObject()
+        \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
+        \note Amortized Constant time complexity.
+    */
+    GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) {
+        GenericValue v(value);
+        return AddMember(name, v, allocator);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Add a string object as member (name-value pair) to the object.
+    /*! \param name A string value as name of member.
+        \param value constant string reference as value of member.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \pre  IsObject()
+        \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
+        \note Amortized Constant time complexity.
+    */
+    GenericValue& AddMember(GenericValue& name, std::basic_string<Ch>& value, Allocator& allocator) {
+        GenericValue v(value, allocator);
+        return AddMember(name, v, allocator);
+    }
+#endif
+
+    //! Add any primitive value as member (name-value pair) to the object.
+    /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+        \param name A string value as name of member.
+        \param value Value of primitive type \c T as value of member
+        \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \pre  IsObject()
+
+        \note The source type \c T explicitly disallows all pointer types,
+            especially (\c const) \ref Ch*.  This helps avoiding implicitly
+            referencing character strings with insufficient lifetime, use
+            \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
+            AddMember(StringRefType, StringRefType, Allocator&).
+            All other pointer types would implicitly convert to \c bool,
+            use an explicit cast instead, if needed.
+        \note Amortized Constant time complexity.
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+    AddMember(GenericValue& name, T value, Allocator& allocator) {
+        GenericValue v(value);
+        return AddMember(name, v, allocator);
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) {
+        return AddMember(name, value, allocator);
+    }
+    GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) {
+        return AddMember(name, value, allocator);
+    }
+    GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) {
+        return AddMember(name, value, allocator);
+    }
+    GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) {
+        GenericValue n(name);
+        return AddMember(n, value, allocator);
+    }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+
+    //! Add a member (name-value pair) to the object.
+    /*! \param name A constant string reference as name of member.
+        \param value Value of any type.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \note The ownership of \c value will be transferred to this object on success.
+        \pre  IsObject()
+        \post value.IsNull()
+        \note Amortized Constant time complexity.
+    */
+    GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) {
+        GenericValue n(name);
+        return AddMember(n, value, allocator);
+    }
+
+    //! Add a constant string value as member (name-value pair) to the object.
+    /*! \param name A constant string reference as name of member.
+        \param value constant string reference as value of member.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \pre  IsObject()
+        \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below.
+        \note Amortized Constant time complexity.
+    */
+    GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) {
+        GenericValue v(value);
+        return AddMember(name, v, allocator);
+    }
+
+    //! Add any primitive value as member (name-value pair) to the object.
+    /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+        \param name A constant string reference as name of member.
+        \param value Value of primitive type \c T as value of member
+        \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \pre  IsObject()
+
+        \note The source type \c T explicitly disallows all pointer types,
+            especially (\c const) \ref Ch*.  This helps avoiding implicitly
+            referencing character strings with insufficient lifetime, use
+            \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
+            AddMember(StringRefType, StringRefType, Allocator&).
+            All other pointer types would implicitly convert to \c bool,
+            use an explicit cast instead, if needed.
+        \note Amortized Constant time complexity.
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+    AddMember(StringRefType name, T value, Allocator& allocator) {
+        GenericValue n(name);
+        return AddMember(n, value, allocator);
+    }
+
+    //! Remove all members in the object.
+    /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged.
+        \note Linear time complexity.
+    */
+    void RemoveAllMembers() {
+        RAPIDJSON_ASSERT(IsObject()); 
+        for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+            m->~Member();
+        data_.o.size = 0;
+    }
+
+    //! Remove a member in object by its name.
+    /*! \param name Name of member to be removed.
+        \return Whether the member existed.
+        \note This function may reorder the object members. Use \ref
+            EraseMember(ConstMemberIterator) if you need to preserve the
+            relative order of the remaining members.
+        \note Linear time complexity.
+    */
+    bool RemoveMember(const Ch* name) {
+        GenericValue n(StringRef(name));
+        return RemoveMember(n);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    bool RemoveMember(const std::basic_string<Ch>& name) { return RemoveMember(GenericValue(StringRef(name))); }
+#endif
+
+    template <typename SourceAllocator>
+    bool RemoveMember(const GenericValue<Encoding, SourceAllocator>& name) {
+        MemberIterator m = FindMember(name);
+        if (m != MemberEnd()) {
+            RemoveMember(m);
+            return true;
+        }
+        else
+            return false;
+    }
+
+    //! Remove a member in object by iterator.
+    /*! \param m member iterator (obtained by FindMember() or MemberBegin()).
+        \return the new iterator after removal.
+        \note This function may reorder the object members. Use \ref
+            EraseMember(ConstMemberIterator) if you need to preserve the
+            relative order of the remaining members.
+        \note Constant time complexity.
+    */
+    MemberIterator RemoveMember(MemberIterator m) {
+        RAPIDJSON_ASSERT(IsObject());
+        RAPIDJSON_ASSERT(data_.o.size > 0);
+        RAPIDJSON_ASSERT(GetMembersPointer() != 0);
+        RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
+
+        MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
+        if (data_.o.size > 1 && m != last)
+            *m = *last; // Move the last one to this place
+        else
+            m->~Member(); // Only one left, just destroy
+        --data_.o.size;
+        return m;
+    }
+
+    //! Remove a member from an object by iterator.
+    /*! \param pos iterator to the member to remove
+        \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd()
+        \return Iterator following the removed element.
+            If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned.
+        \note This function preserves the relative order of the remaining object
+            members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator).
+        \note Linear time complexity.
+    */
+    MemberIterator EraseMember(ConstMemberIterator pos) {
+        return EraseMember(pos, pos +1);
+    }
+
+    //! Remove members in the range [first, last) from an object.
+    /*! \param first iterator to the first member to remove
+        \param last  iterator following the last member to remove
+        \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd()
+        \return Iterator following the last removed element.
+        \note This function preserves the relative order of the remaining object
+            members.
+        \note Linear time complexity.
+    */
+    MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
+        RAPIDJSON_ASSERT(IsObject());
+        RAPIDJSON_ASSERT(data_.o.size > 0);
+        RAPIDJSON_ASSERT(GetMembersPointer() != 0);
+        RAPIDJSON_ASSERT(first >= MemberBegin());
+        RAPIDJSON_ASSERT(first <= last);
+        RAPIDJSON_ASSERT(last <= MemberEnd());
+
+        MemberIterator pos = MemberBegin() + (first - MemberBegin());
+        for (MemberIterator itr = pos; itr != last; ++itr)
+            itr->~Member();
+        std::memmove(&*pos, &*last, static_cast<size_t>(MemberEnd() - last) * sizeof(Member));
+        data_.o.size -= static_cast<SizeType>(last - first);
+        return pos;
+    }
+
+    //! Erase a member in object by its name.
+    /*! \param name Name of member to be removed.
+        \return Whether the member existed.
+        \note Linear time complexity.
+    */
+    bool EraseMember(const Ch* name) {
+        GenericValue n(StringRef(name));
+        return EraseMember(n);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    bool EraseMember(const std::basic_string<Ch>& name) { return EraseMember(GenericValue(StringRef(name))); }
+#endif
+
+    template <typename SourceAllocator>
+    bool EraseMember(const GenericValue<Encoding, SourceAllocator>& name) {
+        MemberIterator m = FindMember(name);
+        if (m != MemberEnd()) {
+            EraseMember(m);
+            return true;
+        }
+        else
+            return false;
+    }
+
+    Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
+    ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
+
+    //@}
+
+    //!@name Array
+    //@{
+
+    //! Set this value as an empty array.
+    /*! \post IsArray == true */
+    GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
+
+    //! Get the number of elements in array.
+    SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; }
+
+    //! Get the capacity of array.
+    SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; }
+
+    //! Check whether the array is empty.
+    bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; }
+
+    //! Remove all elements in the array.
+    /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged.
+        \note Linear time complexity.
+    */
+    void Clear() {
+        RAPIDJSON_ASSERT(IsArray()); 
+        GenericValue* e = GetElementsPointer();
+        for (GenericValue* v = e; v != e + data_.a.size; ++v)
+            v->~GenericValue();
+        data_.a.size = 0;
+    }
+
+    //! Get an element from array by index.
+    /*! \pre IsArray() == true
+        \param index Zero-based index of element.
+        \see operator[](T*)
+    */
+    GenericValue& operator[](SizeType index) {
+        RAPIDJSON_ASSERT(IsArray());
+        RAPIDJSON_ASSERT(index < data_.a.size);
+        return GetElementsPointer()[index];
+    }
+    const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
+
+    //! Element iterator
+    /*! \pre IsArray() == true */
+    ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); }
+    //! \em Past-the-end element iterator
+    /*! \pre IsArray() == true */
+    ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; }
+    //! Constant element iterator
+    /*! \pre IsArray() == true */
+    ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
+    //! Constant \em past-the-end element iterator
+    /*! \pre IsArray() == true */
+    ConstValueIterator End() const { return const_cast<GenericValue&>(*this).End(); }
+
+    //! Request the array to have enough capacity to store elements.
+    /*! \param newCapacity  The capacity that the array at least need to have.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \note Linear time complexity.
+    */
+    GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
+        RAPIDJSON_ASSERT(IsArray());
+        if (newCapacity > data_.a.capacity) {
+            SetElementsPointer(reinterpret_cast<GenericValue*>(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))));
+            data_.a.capacity = newCapacity;
+        }
+        return *this;
+    }
+
+    //! Append a GenericValue at the end of the array.
+    /*! \param value        Value to be appended.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \pre IsArray() == true
+        \post value.IsNull() == true
+        \return The value itself for fluent API.
+        \note The ownership of \c value will be transferred to this array on success.
+        \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+        \note Amortized constant time complexity.
+    */
+    GenericValue& PushBack(GenericValue& value, Allocator& allocator) {
+        RAPIDJSON_ASSERT(IsArray());
+        if (data_.a.size >= data_.a.capacity)
+            Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator);
+        GetElementsPointer()[data_.a.size++].RawAssign(value);
+        return *this;
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericValue& PushBack(GenericValue&& value, Allocator& allocator) {
+        return PushBack(value, allocator);
+    }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+    //! Append a constant string reference at the end of the array.
+    /*! \param value        Constant string reference to be appended.
+        \param allocator    Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator().
+        \pre IsArray() == true
+        \return The value itself for fluent API.
+        \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+        \note Amortized constant time complexity.
+        \see GenericStringRef
+    */
+    GenericValue& PushBack(StringRefType value, Allocator& allocator) {
+        return (*this).template PushBack<StringRefType>(value, allocator);
+    }
+
+    //! Append a primitive value at the end of the array.
+    /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+        \param value Value of primitive type T to be appended.
+        \param allocator    Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+        \pre IsArray() == true
+        \return The value itself for fluent API.
+        \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+
+        \note The source type \c T explicitly disallows all pointer types,
+            especially (\c const) \ref Ch*.  This helps avoiding implicitly
+            referencing character strings with insufficient lifetime, use
+            \ref PushBack(GenericValue&, Allocator&) or \ref
+            PushBack(StringRefType, Allocator&).
+            All other pointer types would implicitly convert to \c bool,
+            use an explicit cast instead, if needed.
+        \note Amortized constant time complexity.
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+    PushBack(T value, Allocator& allocator) {
+        GenericValue v(value);
+        return PushBack(v, allocator);
+    }
+
+    //! Remove the last element in the array.
+    /*!
+        \note Constant time complexity.
+    */
+    GenericValue& PopBack() {
+        RAPIDJSON_ASSERT(IsArray());
+        RAPIDJSON_ASSERT(!Empty());
+        GetElementsPointer()[--data_.a.size].~GenericValue();
+        return *this;
+    }
+
+    //! Remove an element of array by iterator.
+    /*!
+        \param pos iterator to the element to remove
+        \pre IsArray() == true && \ref Begin() <= \c pos < \ref End()
+        \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned.
+        \note Linear time complexity.
+    */
+    ValueIterator Erase(ConstValueIterator pos) {
+        return Erase(pos, pos + 1);
+    }
+
+    //! Remove elements in the range [first, last) of the array.
+    /*!
+        \param first iterator to the first element to remove
+        \param last  iterator following the last element to remove
+        \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End()
+        \return Iterator following the last removed element.
+        \note Linear time complexity.
+    */
+    ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
+        RAPIDJSON_ASSERT(IsArray());
+        RAPIDJSON_ASSERT(data_.a.size > 0);
+        RAPIDJSON_ASSERT(GetElementsPointer() != 0);
+        RAPIDJSON_ASSERT(first >= Begin());
+        RAPIDJSON_ASSERT(first <= last);
+        RAPIDJSON_ASSERT(last <= End());
+        ValueIterator pos = Begin() + (first - Begin());
+        for (ValueIterator itr = pos; itr != last; ++itr)
+            itr->~GenericValue();       
+        std::memmove(pos, last, static_cast<size_t>(End() - last) * sizeof(GenericValue));
+        data_.a.size -= static_cast<SizeType>(last - first);
+        return pos;
+    }
+
+    Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); }
+    ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); }
+
+    //@}
+
+    //!@name Number
+    //@{
+
+    int GetInt() const          { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag);   return data_.n.i.i;   }
+    unsigned GetUint() const    { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag);  return data_.n.u.u;   }
+    int64_t GetInt64() const    { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; }
+    uint64_t GetUint64() const  { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; }
+
+    //! Get the value as double type.
+    /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless.
+    */
+    double GetDouble() const {
+        RAPIDJSON_ASSERT(IsNumber());
+        if ((data_.f.flags & kDoubleFlag) != 0)                return data_.n.d;   // exact type, no conversion.
+        if ((data_.f.flags & kIntFlag) != 0)                   return data_.n.i.i; // int -> double
+        if ((data_.f.flags & kUintFlag) != 0)                  return data_.n.u.u; // unsigned -> double
+        if ((data_.f.flags & kInt64Flag) != 0)                 return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
+        RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0);  return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
+    }
+
+    //! Get the value as float type.
+    /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless.
+    */
+    float GetFloat() const {
+        return static_cast<float>(GetDouble());
+    }
+
+    GenericValue& SetInt(int i)             { this->~GenericValue(); new (this) GenericValue(i);    return *this; }
+    GenericValue& SetUint(unsigned u)       { this->~GenericValue(); new (this) GenericValue(u);    return *this; }
+    GenericValue& SetInt64(int64_t i64)     { this->~GenericValue(); new (this) GenericValue(i64);  return *this; }
+    GenericValue& SetUint64(uint64_t u64)   { this->~GenericValue(); new (this) GenericValue(u64);  return *this; }
+    GenericValue& SetDouble(double d)       { this->~GenericValue(); new (this) GenericValue(d);    return *this; }
+    GenericValue& SetFloat(float f)         { this->~GenericValue(); new (this) GenericValue(static_cast<double>(f)); return *this; }
+
+    //@}
+
+    //!@name String
+    //@{
+
+    const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
+
+    //! Get the length of string.
+    /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
+    */
+    SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
+
+    //! Set this value as a string without copying source string.
+    /*! This version has better performance with supplied length, and also support string containing null character.
+        \param s source string pointer. 
+        \param length The length of source string, excluding the trailing null terminator.
+        \return The value itself for fluent API.
+        \post IsString() == true && GetString() == s && GetStringLength() == length
+        \see SetString(StringRefType)
+    */
+    GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); }
+
+    //! Set this value as a string without copying source string.
+    /*! \param s source string reference
+        \return The value itself for fluent API.
+        \post IsString() == true && GetString() == s && GetStringLength() == s.length
+    */
+    GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; }
+
+    //! Set this value as a string by copying from source string.
+    /*! This version has better performance with supplied length, and also support string containing null character.
+        \param s source string. 
+        \param length The length of source string, excluding the trailing null terminator.
+        \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+    */
+    GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); }
+
+    //! Set this value as a string by copying from source string.
+    /*! \param s source string. 
+        \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+    */
+    GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); }
+
+    //! Set this value as a string by copying from source string.
+    /*! \param s source string reference
+        \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+    */
+    GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Set this value as a string by copying from source string.
+    /*! \param s source string.
+        \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+        \return The value itself for fluent API.
+        \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size()
+        \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+    */
+    GenericValue& SetString(const std::basic_string<Ch>& s, Allocator& allocator) { return SetString(StringRef(s), allocator); }
+#endif
+
+    //@}
+
+    //!@name Array
+    //@{
+
+    //! Templated version for checking whether this value is type T.
+    /*!
+        \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string<Ch>
+    */
+    template <typename T>
+    bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); }
+
+    template <typename T>
+    T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+    template <typename T>
+    T Get() { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+    template<typename T>
+    ValueType& Set(const T& data) { return internal::TypeHelper<ValueType, T>::Set(*this, data); }
+
+    template<typename T>
+    ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); }
+
+    //@}
+
+    //! Generate events of this value to a Handler.
+    /*! This function adopts the GoF visitor pattern.
+        Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
+        It can also be used to deep clone this value via GenericDocument, which is also a Handler.
+        \tparam Handler type of handler.
+        \param handler An object implementing concept Handler.
+    */
+    template <typename Handler>
+    bool Accept(Handler& handler) const {
+        switch(GetType()) {
+        case kNullType:     return handler.Null();
+        case kFalseType:    return handler.Bool(false);
+        case kTrueType:     return handler.Bool(true);
+
+        case kObjectType:
+            if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
+                return false;
+            for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
+                RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator.
+                if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0)))
+                    return false;
+                if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler)))
+                    return false;
+            }
+            return handler.EndObject(data_.o.size);
+
+        case kArrayType:
+            if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
+                return false;
+            for (const GenericValue* v = Begin(); v != End(); ++v)
+                if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
+                    return false;
+            return handler.EndArray(data_.a.size);
+    
+        case kStringType:
+            return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
+    
+        default:
+            RAPIDJSON_ASSERT(GetType() == kNumberType);
+            if (IsDouble())         return handler.Double(data_.n.d);
+            else if (IsInt())       return handler.Int(data_.n.i.i);
+            else if (IsUint())      return handler.Uint(data_.n.u.u);
+            else if (IsInt64())     return handler.Int64(data_.n.i64);
+            else                    return handler.Uint64(data_.n.u64);
+        }
+    }
+
+private:
+    template <typename, typename> friend class GenericValue;
+    template <typename, typename, typename> friend class GenericDocument;
+
+    enum {
+        kBoolFlag       = 0x0008,
+        kNumberFlag     = 0x0010,
+        kIntFlag        = 0x0020,
+        kUintFlag       = 0x0040,
+        kInt64Flag      = 0x0080,
+        kUint64Flag     = 0x0100,
+        kDoubleFlag     = 0x0200,
+        kStringFlag     = 0x0400,
+        kCopyFlag       = 0x0800,
+        kInlineStrFlag  = 0x1000,
+
+        // Initial flags of different types.
+        kNullFlag = kNullType,
+        kTrueFlag = kTrueType | kBoolFlag,
+        kFalseFlag = kFalseType | kBoolFlag,
+        kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag,
+        kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag,
+        kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag,
+        kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag,
+        kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag,
+        kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag,
+        kConstStringFlag = kStringType | kStringFlag,
+        kCopyStringFlag = kStringType | kStringFlag | kCopyFlag,
+        kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag,
+        kObjectFlag = kObjectType,
+        kArrayFlag = kArrayType,
+
+        kTypeMask = 0x07
+    };
+
+    static const SizeType kDefaultArrayCapacity = 16;
+    static const SizeType kDefaultObjectCapacity = 16;
+
+    struct Flag {
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
+        char payload[sizeof(SizeType) * 2 + 6];     // 2 x SizeType + lower 48-bit pointer
+#elif RAPIDJSON_64BIT
+        char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes
+#else
+        char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes
+#endif
+        uint16_t flags;
+    };
+
+    struct String {
+        SizeType length;
+        SizeType hashcode;  //!< reserved
+        const Ch* str;
+    };  // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+    // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars
+    // (excluding the terminating zero) and store a value to determine the length of the contained
+    // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string
+    // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as
+    // the string terminator as well. For getting the string length back from that value just use
+    // "MaxSize - str[LenPos]".
+    // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode,
+    // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings).
+    struct ShortString {
+        enum { MaxChars = sizeof(static_cast<Flag*>(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
+        Ch str[MaxChars];
+
+        inline static bool Usable(SizeType len) { return                       (MaxSize >= len); }
+        inline void     SetLength(SizeType len) { str[LenPos] = static_cast<Ch>(MaxSize -  len); }
+        inline SizeType GetLength() const       { return  static_cast<SizeType>(MaxSize -  str[LenPos]); }
+    };  // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+    // By using proper binary layout, retrieval of different integer types do not need conversions.
+    union Number {
+#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN
+        struct I {
+            int i;
+            char padding[4];
+        }i;
+        struct U {
+            unsigned u;
+            char padding2[4];
+        }u;
+#else
+        struct I {
+            char padding[4];
+            int i;
+        }i;
+        struct U {
+            char padding2[4];
+            unsigned u;
+        }u;
+#endif
+        int64_t i64;
+        uint64_t u64;
+        double d;
+    };  // 8 bytes
+
+    struct ObjectData {
+        SizeType size;
+        SizeType capacity;
+        Member* members;
+    };  // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+    struct ArrayData {
+        SizeType size;
+        SizeType capacity;
+        GenericValue* elements;
+    };  // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+    union Data {
+        String s;
+        ShortString ss;
+        Number n;
+        ObjectData o;
+        ArrayData a;
+        Flag f;
+    };  // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+    RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
+    RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
+    RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
+    RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); }
+    RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
+    RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
+
+    // Initialize this value as array with initial data, without calling destructor.
+    void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
+        data_.f.flags = kArrayFlag;
+        if (count) {
+            GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
+            SetElementsPointer(e);
+            std::memcpy(e, values, count * sizeof(GenericValue));
+        }
+        else
+            SetElementsPointer(0);
+        data_.a.size = data_.a.capacity = count;
+    }
+
+    //! Initialize this value as object with initial data, without calling destructor.
+    void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
+        data_.f.flags = kObjectFlag;
+        if (count) {
+            Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
+            SetMembersPointer(m);
+            std::memcpy(m, members, count * sizeof(Member));
+        }
+        else
+            SetMembersPointer(0);
+        data_.o.size = data_.o.capacity = count;
+    }
+
+    //! Initialize this value as constant string, without calling destructor.
+    void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
+        data_.f.flags = kConstStringFlag;
+        SetStringPointer(s);
+        data_.s.length = s.length;
+    }
+
+    //! Initialize this value as copy string with initial data, without calling destructor.
+    void SetStringRaw(StringRefType s, Allocator& allocator) {
+        Ch* str = 0;
+        if (ShortString::Usable(s.length)) {
+            data_.f.flags = kShortStringFlag;
+            data_.ss.SetLength(s.length);
+            str = data_.ss.str;
+        } else {
+            data_.f.flags = kCopyStringFlag;
+            data_.s.length = s.length;
+            str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch)));
+            SetStringPointer(str);
+        }
+        std::memcpy(str, s, s.length * sizeof(Ch));
+        str[s.length] = '\0';
+    }
+
+    //! Assignment without calling destructor
+    void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
+        data_ = rhs.data_;
+        // data_.f.flags = rhs.data_.f.flags;
+        rhs.data_.f.flags = kNullFlag;
+    }
+
+    template <typename SourceAllocator>
+    bool StringEqual(const GenericValue<Encoding, SourceAllocator>& rhs) const {
+        RAPIDJSON_ASSERT(IsString());
+        RAPIDJSON_ASSERT(rhs.IsString());
+
+        const SizeType len1 = GetStringLength();
+        const SizeType len2 = rhs.GetStringLength();
+        if(len1 != len2) { return false; }
+
+        const Ch* const str1 = GetString();
+        const Ch* const str2 = rhs.GetString();
+        if(str1 == str2) { return true; } // fast path for constant string
+
+        return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0);
+    }
+
+    Data data_;
+};
+
+//! GenericValue with UTF8 encoding
+typedef GenericValue<UTF8<> > Value;
+
+typedef GenericValue<UTF8<> > JsonObj;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericDocument 
+
+//! A document for parsing JSON text as DOM.
+/*!
+    \note implements Handler concept
+    \tparam Encoding Encoding for both parsing and string storage.
+    \tparam Allocator Allocator for allocating memory for the DOM
+    \tparam StackAllocator Allocator for allocating memory for stack during parsing.
+    \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor.  To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue.
+*/
+template <typename Encoding, typename Allocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator>
+class GenericDocument : public GenericValue<Encoding, Allocator> {
+public:
+    typedef typename Encoding::Ch Ch;                       //!< Character type derived from Encoding.
+    typedef GenericValue<Encoding, Allocator> ValueType;    //!< Value type of the document.
+    typedef Allocator AllocatorType;                        //!< Allocator type from template parameter.
+
+    //! Constructor
+    /*! Creates an empty document of specified type.
+        \param type             Mandatory type of object to create.
+        \param allocator        Optional allocator for allocating memory.
+        \param stackCapacity    Optional initial capacity of stack in bytes.
+        \param stackAllocator   Optional allocator for allocating memory for stack.
+    */
+    explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) :
+        GenericValue<Encoding, Allocator>(type),  allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
+    {
+        if (!allocator_)
+            ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+    }
+
+    //! Constructor
+    /*! Creates an empty document which type is Null. 
+        \param allocator        Optional allocator for allocating memory.
+        \param stackCapacity    Optional initial capacity of stack in bytes.
+        \param stackAllocator   Optional allocator for allocating memory for stack.
+    */
+    GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : 
+        allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
+    {
+        if (!allocator_)
+            ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Move constructor in C++11
+    GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
+        : ValueType(std::forward<ValueType>(rhs)), // explicit cast to avoid prohibited move from Document
+          allocator_(rhs.allocator_),
+          ownAllocator_(rhs.ownAllocator_),
+          stack_(std::move(rhs.stack_)),
+          parseResult_(rhs.parseResult_)
+    {
+        rhs.allocator_ = 0;
+        rhs.ownAllocator_ = 0;
+        rhs.parseResult_ = ParseResult();
+    }
+#endif
+
+    ~GenericDocument() {
+        Destroy();
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Move assignment in C++11
+    GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
+    {
+        // The cast to ValueType is necessary here, because otherwise it would
+        // attempt to call GenericValue's templated assignment operator.
+        ValueType::operator=(std::forward<ValueType>(rhs));
+
+        // Calling the destructor here would prematurely call stack_'s destructor
+        Destroy();
+
+        allocator_ = rhs.allocator_;
+        ownAllocator_ = rhs.ownAllocator_;
+        stack_ = std::move(rhs.stack_);
+        parseResult_ = rhs.parseResult_;
+
+        rhs.allocator_ = 0;
+        rhs.ownAllocator_ = 0;
+        rhs.parseResult_ = ParseResult();
+
+        return *this;
+    }
+#endif
+
+    //! Exchange the contents of this document with those of another.
+    /*!
+        \param rhs Another document.
+        \note Constant complexity.
+        \see GenericValue::Swap
+    */
+    GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT {
+        ValueType::Swap(rhs);
+        stack_.Swap(rhs.stack_);
+        internal::Swap(allocator_, rhs.allocator_);
+        internal::Swap(ownAllocator_, rhs.ownAllocator_);
+        internal::Swap(parseResult_, rhs.parseResult_);
+        return *this;
+    }
+
+    // Allow Swap with ValueType.
+    // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names.
+    using ValueType::Swap;
+
+    //! free-standing swap function helper
+    /*!
+        Helper function to enable support for common swap implementation pattern based on \c std::swap:
+        \code
+        void swap(MyClass& a, MyClass& b) {
+            using std::swap;
+            swap(a.doc, b.doc);
+            // ...
+        }
+        \endcode
+        \see Swap()
+     */
+    friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+
+    //! Populate this document by a generator which produces SAX events.
+    /*! \tparam Generator A functor with <tt>bool f(Handler)</tt> prototype.
+        \param g Generator functor which sends SAX events to the parameter.
+        \return The document itself for fluent API.
+    */
+    template <typename Generator>
+    GenericDocument& Populate(Generator& g) {
+        ClearStackOnExit scope(*this);
+        if (g(*this)) {
+            RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+            ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+        }
+        return *this;
+    }
+
+    //!@name Parse from stream
+    //!@{
+
+    //! Parse JSON text from an input stream (with Encoding conversion)
+    /*! \tparam parseFlags Combination of \ref ParseFlag.
+        \tparam SourceEncoding Encoding of input stream
+        \tparam InputStream Type of input stream, implementing Stream concept
+        \param is Input stream to be parsed.
+        \return The document itself for fluent API.
+    */
+    template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
+    GenericDocument& ParseStream(InputStream& is) {
+        GenericReader<SourceEncoding, Encoding, StackAllocator> reader(
+            stack_.HasAllocator() ? &stack_.GetAllocator() : 0);
+        ClearStackOnExit scope(*this);
+        parseResult_ = reader.template Parse<parseFlags>(is, *this);
+        if (parseResult_) {
+            RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+            ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+        }
+        return *this;
+    }
+
+    //! Parse JSON text from an input stream
+    /*! \tparam parseFlags Combination of \ref ParseFlag.
+        \tparam InputStream Type of input stream, implementing Stream concept
+        \param is Input stream to be parsed.
+        \return The document itself for fluent API.
+    */
+    template <unsigned parseFlags, typename InputStream>
+    GenericDocument& ParseStream(InputStream& is) {
+        return ParseStream<parseFlags, Encoding, InputStream>(is);
+    }
+
+    //! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
+    /*! \tparam InputStream Type of input stream, implementing Stream concept
+        \param is Input stream to be parsed.
+        \return The document itself for fluent API.
+    */
+    template <typename InputStream>
+    GenericDocument& ParseStream(InputStream& is) {
+        return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is);
+    }
+    //!@}
+
+    //!@name Parse in-place from mutable string
+    //!@{
+
+    //! Parse JSON text from a mutable string
+    /*! \tparam parseFlags Combination of \ref ParseFlag.
+        \param str Mutable zero-terminated string to be parsed.
+        \return The document itself for fluent API.
+    */
+    template <unsigned parseFlags>
+    GenericDocument& ParseInsitu(Ch* str) {
+        GenericInsituStringStream<Encoding> s(str);
+        return ParseStream<parseFlags | kParseInsituFlag>(s);
+    }
+
+    //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags)
+    /*! \param str Mutable zero-terminated string to be parsed.
+        \return The document itself for fluent API.
+    */
+    GenericDocument& ParseInsitu(Ch* str) {
+        return ParseInsitu<kParseDefaultFlags>(str);
+    }
+    //!@}
+
+    //!@name Parse from read-only string
+    //!@{
+
+    //! Parse JSON text from a read-only string (with Encoding conversion)
+    /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
+        \tparam SourceEncoding Transcoding from input Encoding
+        \param str Read-only zero-terminated string to be parsed.
+    */
+    template <unsigned parseFlags, typename SourceEncoding>
+    GenericDocument& Parse(const typename SourceEncoding::Ch* str) {
+        RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+        GenericStringStream<SourceEncoding> s(str);
+        return ParseStream<parseFlags, SourceEncoding>(s);
+    }
+
+    //! Parse JSON text from a read-only string
+    /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
+        \param str Read-only zero-terminated string to be parsed.
+    */
+    template <unsigned parseFlags>
+    GenericDocument& Parse(const Ch* str) {
+        return Parse<parseFlags, Encoding>(str);
+    }
+
+    //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags)
+    /*! \param str Read-only zero-terminated string to be parsed.
+    */
+    GenericDocument& Parse(const Ch* str) {
+        return Parse<kParseDefaultFlags>(str);
+    }
+
+    template <unsigned parseFlags, typename SourceEncoding>
+    GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) {
+        RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+        MemoryStream ms(reinterpret_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch));
+        EncodedInputStream<SourceEncoding, MemoryStream> is(ms);
+        ParseStream<parseFlags, SourceEncoding>(is);
+        return *this;
+    }
+
+    template <unsigned parseFlags>
+    GenericDocument& Parse(const Ch* str, size_t length) {
+        return Parse<parseFlags, Encoding>(str, length);
+    }
+    
+    GenericDocument& Parse(const Ch* str, size_t length) {
+        return Parse<kParseDefaultFlags>(str, length);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    template <unsigned parseFlags, typename SourceEncoding>
+    GenericDocument& Parse(const std::basic_string<typename SourceEncoding::Ch>& str) {
+        // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t)
+        return Parse<parseFlags, SourceEncoding>(str.c_str());
+    }
+
+    template <unsigned parseFlags>
+    GenericDocument& Parse(const std::basic_string<Ch>& str) {
+        return Parse<parseFlags, Encoding>(str.c_str());
+    }
+
+    GenericDocument& Parse(const std::basic_string<Ch>& str) {
+        return Parse<kParseDefaultFlags>(str);
+    }
+#endif // RAPIDJSON_HAS_STDSTRING    
+
+    //!@}
+
+    //!@name Handling parse errors
+    //!@{
+
+    //! Whether a parse error has occured in the last parsing.
+    bool HasParseError() const { return parseResult_.IsError(); }
+
+    //! Get the \ref ParseErrorCode of last parsing.
+    ParseErrorCode GetParseError() const { return parseResult_.Code(); }
+
+    //! Get the position of last parsing error in input, 0 otherwise.
+    size_t GetErrorOffset() const { return parseResult_.Offset(); }
+
+    //! Implicit conversion to get the last parse result
+#ifndef __clang // -Wdocumentation
+    /*! \return \ref ParseResult of the last parse operation
+
+        \code
+          Document doc;
+          ParseResult ok = doc.Parse(json);
+          if (!ok)
+            printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset());
+        \endcode
+     */
+#endif
+    operator ParseResult() const { return parseResult_; }
+    //!@}
+
+    //! Get the allocator of this document.
+    Allocator& GetAllocator() {
+        RAPIDJSON_ASSERT(allocator_);
+        return *allocator_;
+    }
+
+    //! Get the capacity of stack in bytes.
+    size_t GetStackCapacity() const { return stack_.GetCapacity(); }
+
+private:
+    // clear stack on any exit from ParseStream, e.g. due to exception
+    struct ClearStackOnExit {
+        explicit ClearStackOnExit(GenericDocument& d) : d_(d) {}
+        ~ClearStackOnExit() { d_.ClearStack(); }
+    private:
+        ClearStackOnExit(const ClearStackOnExit&);
+        ClearStackOnExit& operator=(const ClearStackOnExit&);
+        GenericDocument& d_;
+    };
+
+    // callers of the following private Handler functions
+    // template <typename,typename,typename> friend class GenericReader; // for parsing
+    template <typename, typename> friend class GenericValue; // for deep copying
+
+public:
+    // Implementation of Handler
+    bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
+    bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
+    bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+    bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+    bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+    bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+    bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
+
+    bool RawNumber(const Ch* str, SizeType length, bool copy) { 
+        if (copy) 
+            new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+        else
+            new (stack_.template Push<ValueType>()) ValueType(str, length);
+        return true;
+    }
+
+    bool String(const Ch* str, SizeType length, bool copy) { 
+        if (copy) 
+            new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+        else
+            new (stack_.template Push<ValueType>()) ValueType(str, length);
+        return true;
+    }
+
+    bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; }
+    
+    bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); }
+
+    bool EndObject(SizeType memberCount) {
+        typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount);
+        stack_.template Top<ValueType>()->SetObjectRaw(members, memberCount, GetAllocator());
+        return true;
+    }
+
+    bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; }
+    
+    bool EndArray(SizeType elementCount) {
+        ValueType* elements = stack_.template Pop<ValueType>(elementCount);
+        stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator());
+        return true;
+    }
+
+private:
+    //! Prohibit copying
+    GenericDocument(const GenericDocument&);
+    //! Prohibit assignment
+    GenericDocument& operator=(const GenericDocument&);
+
+    void ClearStack() {
+        if (Allocator::kNeedFree)
+            while (stack_.GetSize() > 0)    // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects)
+                (stack_.template Pop<ValueType>(1))->~ValueType();
+        else
+            stack_.Clear();
+        stack_.ShrinkToFit();
+    }
+
+    void Destroy() {
+        RAPIDJSON_DELETE(ownAllocator_);
+    }
+
+    static const size_t kDefaultStackCapacity = 1024;
+    Allocator* allocator_;
+    Allocator* ownAllocator_;
+    internal::Stack<StackAllocator> stack_;
+    ParseResult parseResult_;
+};
+
+//! GenericDocument with UTF8 encoding
+typedef GenericDocument<UTF8<> > Document;
+typedef GenericDocument<UTF8<> > JsonDoc;
+
+
+//! Helper class for accessing Value of array type.
+/*!
+    Instance of this helper class is obtained by \c GenericValue::GetArray().
+    In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericArray {
+public:
+    typedef GenericArray<true, ValueT> ConstArray;
+    typedef GenericArray<false, ValueT> Array;
+    typedef ValueT PlainType;
+    typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+    typedef ValueType* ValueIterator;  // This may be const or non-const iterator
+    typedef const ValueT* ConstValueIterator;
+    typedef typename ValueType::AllocatorType AllocatorType;
+    typedef typename ValueType::StringRefType StringRefType;
+
+    template <typename, typename>
+    friend class GenericValue;
+
+    GenericArray(const GenericArray& rhs) : value_(rhs.value_) {}
+    GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
+    ~GenericArray() {}
+
+    SizeType Size() const { return value_.Size(); }
+    SizeType Capacity() const { return value_.Capacity(); }
+    bool Empty() const { return value_.Empty(); }
+    void Clear() const { value_.Clear(); }
+    ValueType& operator[](SizeType index) const {  return value_[index]; }
+    ValueIterator Begin() const { return value_.Begin(); }
+    ValueIterator End() const { return value_.End(); }
+    GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; }
+    GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+    template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+    GenericArray PopBack() const { value_.PopBack(); return *this; }
+    ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); }
+    ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+    ValueIterator begin() const { return value_.Begin(); }
+    ValueIterator end() const { return value_.End(); }
+#endif
+
+private:
+    GenericArray();
+    GenericArray(ValueType& value) : value_(value) {}
+    ValueType& value_;
+};
+
+//! Helper class for accessing Value of object type.
+/*!
+    Instance of this helper class is obtained by \c GenericValue::GetObject().
+    In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericObject {
+public:
+    typedef GenericObject<true, ValueT> ConstObject;
+    typedef GenericObject<false, ValueT> Object;
+    typedef ValueT PlainType;
+    typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+    typedef GenericMemberIterator<Const, typename ValueT::EncodingType, typename ValueT::AllocatorType> MemberIterator;  // This may be const or non-const iterator
+    typedef GenericMemberIterator<true, typename ValueT::EncodingType, typename ValueT::AllocatorType> ConstMemberIterator;
+    typedef typename ValueType::AllocatorType AllocatorType;
+    typedef typename ValueType::StringRefType StringRefType;
+    typedef typename ValueType::EncodingType EncodingType;
+    typedef typename ValueType::Ch Ch;
+
+    template <typename, typename>
+    friend class GenericValue;
+
+    GenericObject(const GenericObject& rhs) : value_(rhs.value_) {}
+    GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
+    ~GenericObject() {}
+
+    SizeType MemberCount() const { return value_.MemberCount(); }
+    SizeType MemberCapacity() const { return value_.MemberCapacity(); }
+    bool ObjectEmpty() const { return value_.ObjectEmpty(); }
+    template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
+    template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
+#if RAPIDJSON_HAS_STDSTRING
+    ValueType& operator[](const std::basic_string<Ch>& name) const { return value_[name]; }
+#endif
+    MemberIterator MemberBegin() const { return value_.MemberBegin(); }
+    MemberIterator MemberEnd() const { return value_.MemberEnd(); }
+    GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; }
+    bool HasMember(const Ch* name) const { return value_.HasMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+    bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
+#endif
+    template <typename SourceAllocator> bool HasMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.HasMember(name); }
+    MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); }
+    template <typename SourceAllocator> MemberIterator FindMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.FindMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+    MemberIterator FindMember(const std::basic_string<Ch>& name) const { return value_.FindMember(name); }
+#endif
+    GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_STDSTRING
+    GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif
+    template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+    void RemoveAllMembers() { value_.RemoveAllMembers(); }
+    bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+    bool RemoveMember(const std::basic_string<Ch>& name) const { return value_.RemoveMember(name); }
+#endif
+    template <typename SourceAllocator> bool RemoveMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.RemoveMember(name); }
+    MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); }
+    MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); }
+    MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); }
+    bool EraseMember(const Ch* name) const { return value_.EraseMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+    bool EraseMember(const std::basic_string<Ch>& name) const { return EraseMember(ValueType(StringRef(name))); }
+#endif
+    template <typename SourceAllocator> bool EraseMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.EraseMember(name); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+    MemberIterator begin() const { return value_.MemberBegin(); }
+    MemberIterator end() const { return value_.MemberEnd(); }
+#endif
+
+private:
+    GenericObject();
+    GenericObject(ValueType& value) : value_(value) {}
+    ValueType& value_;
+};
+
+RAPIDJSON_NAMESPACE_END
+RAPIDJSON_DIAG_POP
+
+#endif // RAPIDJSON_DOCUMENT_H_
diff --git a/src/lottie/rapidjson/encodedstream.h b/src/lottie/rapidjson/encodedstream.h
new file mode 100644 (file)
index 0000000..223601c
--- /dev/null
@@ -0,0 +1,299 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ENCODEDSTREAM_H_
+#define RAPIDJSON_ENCODEDSTREAM_H_
+
+#include "stream.h"
+#include "memorystream.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Input byte stream wrapper with a statically bound encoding.
+/*!
+    \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
+    \tparam InputByteStream Type of input byte stream. For example, FileReadStream.
+*/
+template <typename Encoding, typename InputByteStream>
+class EncodedInputStream {
+    RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+public:
+    typedef typename Encoding::Ch Ch;
+
+    EncodedInputStream(InputByteStream& is) : is_(is) { 
+        current_ = Encoding::TakeBOM(is_);
+    }
+
+    Ch Peek() const { return current_; }
+    Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
+    size_t Tell() const { return is_.Tell(); }
+
+    // Not implemented
+    void Put(Ch) { RAPIDJSON_ASSERT(false); }
+    void Flush() { RAPIDJSON_ASSERT(false); } 
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+    EncodedInputStream(const EncodedInputStream&);
+    EncodedInputStream& operator=(const EncodedInputStream&);
+
+    InputByteStream& is_;
+    Ch current_;
+};
+
+//! Specialized for UTF8 MemoryStream.
+template <>
+class EncodedInputStream<UTF8<>, MemoryStream> {
+public:
+    typedef UTF8<>::Ch Ch;
+
+    EncodedInputStream(MemoryStream& is) : is_(is) {
+        if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
+        if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
+        if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
+    }
+    Ch Peek() const { return is_.Peek(); }
+    Ch Take() { return is_.Take(); }
+    size_t Tell() const { return is_.Tell(); }
+
+    // Not implemented
+    void Put(Ch) {}
+    void Flush() {} 
+    Ch* PutBegin() { return 0; }
+    size_t PutEnd(Ch*) { return 0; }
+
+    MemoryStream& is_;
+
+private:
+    EncodedInputStream(const EncodedInputStream&);
+    EncodedInputStream& operator=(const EncodedInputStream&);
+};
+
+//! Output byte stream wrapper with statically bound encoding.
+/*!
+    \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
+    \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
+*/
+template <typename Encoding, typename OutputByteStream>
+class EncodedOutputStream {
+    RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+public:
+    typedef typename Encoding::Ch Ch;
+
+    EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { 
+        if (putBOM)
+            Encoding::PutBOM(os_);
+    }
+
+    void Put(Ch c) { Encoding::Put(os_, c);  }
+    void Flush() { os_.Flush(); }
+
+    // Not implemented
+    Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
+    Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
+    size_t Tell() const { RAPIDJSON_ASSERT(false);  return 0; }
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+    EncodedOutputStream(const EncodedOutputStream&);
+    EncodedOutputStream& operator=(const EncodedOutputStream&);
+
+    OutputByteStream& os_;
+};
+
+#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
+
+//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
+/*!
+    \tparam CharType Type of character for reading.
+    \tparam InputByteStream type of input byte stream to be wrapped.
+*/
+template <typename CharType, typename InputByteStream>
+class AutoUTFInputStream {
+    RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+public:
+    typedef CharType Ch;
+
+    //! Constructor.
+    /*!
+        \param is input stream to be wrapped.
+        \param type UTF encoding type if it is not detected from the stream.
+    */
+    AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
+        RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);        
+        DetectType();
+        static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
+        takeFunc_ = f[type_];
+        current_ = takeFunc_(*is_);
+    }
+
+    UTFType GetType() const { return type_; }
+    bool HasBOM() const { return hasBOM_; }
+
+    Ch Peek() const { return current_; }
+    Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
+    size_t Tell() const { return is_->Tell(); }
+
+    // Not implemented
+    void Put(Ch) { RAPIDJSON_ASSERT(false); }
+    void Flush() { RAPIDJSON_ASSERT(false); } 
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+    AutoUTFInputStream(const AutoUTFInputStream&);
+    AutoUTFInputStream& operator=(const AutoUTFInputStream&);
+
+    // Detect encoding type with BOM or RFC 4627
+    void DetectType() {
+        // BOM (Byte Order Mark):
+        // 00 00 FE FF  UTF-32BE
+        // FF FE 00 00  UTF-32LE
+        // FE FF        UTF-16BE
+        // FF FE        UTF-16LE
+        // EF BB BF     UTF-8
+
+        const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
+        if (!c)
+            return;
+
+        unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
+        hasBOM_ = false;
+        if (bom == 0xFFFE0000)                  { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
+        else if (bom == 0x0000FEFF)             { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
+        else if ((bom & 0xFFFF) == 0xFFFE)      { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take();                           }
+        else if ((bom & 0xFFFF) == 0xFEFF)      { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take();                           }
+        else if ((bom & 0xFFFFFF) == 0xBFBBEF)  { type_ = kUTF8;    hasBOM_ = true; is_->Take(); is_->Take(); is_->Take();              }
+
+        // RFC 4627: Section 3
+        // "Since the first two characters of a JSON text will always be ASCII
+        // characters [RFC0020], it is possible to determine whether an octet
+        // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
+        // at the pattern of nulls in the first four octets."
+        // 00 00 00 xx  UTF-32BE
+        // 00 xx 00 xx  UTF-16BE
+        // xx 00 00 00  UTF-32LE
+        // xx 00 xx 00  UTF-16LE
+        // xx xx xx xx  UTF-8
+
+        if (!hasBOM_) {
+            int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
+            switch (pattern) {
+            case 0x08: type_ = kUTF32BE; break;
+            case 0x0A: type_ = kUTF16BE; break;
+            case 0x01: type_ = kUTF32LE; break;
+            case 0x05: type_ = kUTF16LE; break;
+            case 0x0F: type_ = kUTF8;    break;
+            default: break; // Use type defined by user.
+            }
+        }
+
+        // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
+        if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
+        if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
+    }
+
+    typedef Ch (*TakeFunc)(InputByteStream& is);
+    InputByteStream* is_;
+    UTFType type_;
+    Ch current_;
+    TakeFunc takeFunc_;
+    bool hasBOM_;
+};
+
+//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
+/*!
+    \tparam CharType Type of character for writing.
+    \tparam OutputByteStream type of output byte stream to be wrapped.
+*/
+template <typename CharType, typename OutputByteStream>
+class AutoUTFOutputStream {
+    RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+public:
+    typedef CharType Ch;
+
+    //! Constructor.
+    /*!
+        \param os output stream to be wrapped.
+        \param type UTF encoding type.
+        \param putBOM Whether to write BOM at the beginning of the stream.
+    */
+    AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
+        RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
+
+        // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
+        if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
+        if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
+
+        static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
+        putFunc_ = f[type_];
+
+        if (putBOM)
+            PutBOM();
+    }
+
+    UTFType GetType() const { return type_; }
+
+    void Put(Ch c) { putFunc_(*os_, c); }
+    void Flush() { os_->Flush(); } 
+
+    // Not implemented
+    Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
+    Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
+    size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+    AutoUTFOutputStream(const AutoUTFOutputStream&);
+    AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
+
+    void PutBOM() { 
+        typedef void (*PutBOMFunc)(OutputByteStream&);
+        static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
+        f[type_](*os_);
+    }
+
+    typedef void (*PutFunc)(OutputByteStream&, Ch);
+
+    OutputByteStream* os_;
+    UTFType type_;
+    PutFunc putFunc_;
+};
+
+#undef RAPIDJSON_ENCODINGS_FUNC
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/src/lottie/rapidjson/encodings.h b/src/lottie/rapidjson/encodings.h
new file mode 100644 (file)
index 0000000..7903e76
--- /dev/null
@@ -0,0 +1,716 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ENCODINGS_H_
+#define RAPIDJSON_ENCODINGS_H_
+
+#include "rapidjson.h"
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
+RAPIDJSON_DIAG_OFF(4702)  // unreachable code
+#elif defined(__GNUC__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(overflow)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Encoding
+
+/*! \class rapidjson::Encoding
+    \brief Concept for encoding of Unicode characters.
+
+\code
+concept Encoding {
+    typename Ch;    //! Type of character. A "character" is actually a code unit in unicode's definition.
+
+    enum { supportUnicode = 1 }; // or 0 if not supporting unicode
+
+    //! \brief Encode a Unicode codepoint to an output stream.
+    //! \param os Output stream.
+    //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
+    template<typename OutputStream>
+    static void Encode(OutputStream& os, unsigned codepoint);
+
+    //! \brief Decode a Unicode codepoint from an input stream.
+    //! \param is Input stream.
+    //! \param codepoint Output of the unicode codepoint.
+    //! \return true if a valid codepoint can be decoded from the stream.
+    template <typename InputStream>
+    static bool Decode(InputStream& is, unsigned* codepoint);
+
+    //! \brief Validate one Unicode codepoint from an encoded stream.
+    //! \param is Input stream to obtain codepoint.
+    //! \param os Output for copying one codepoint.
+    //! \return true if it is valid.
+    //! \note This function just validating and copying the codepoint without actually decode it.
+    template <typename InputStream, typename OutputStream>
+    static bool Validate(InputStream& is, OutputStream& os);
+
+    // The following functions are deal with byte streams.
+
+    //! Take a character from input byte stream, skip BOM if exist.
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is);
+
+    //! Take a character from input byte stream.
+    template <typename InputByteStream>
+    static Ch Take(InputByteStream& is);
+
+    //! Put BOM to output byte stream.
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os);
+
+    //! Put a character to output byte stream.
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, Ch c);
+};
+\endcode
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF8
+
+//! UTF-8 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-8
+    http://tools.ietf.org/html/rfc3629
+    \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
+    \note implements Encoding concept
+*/
+template<typename CharType = char>
+struct UTF8 {
+    typedef CharType Ch;
+
+    enum { supportUnicode = 1 };
+
+    template<typename OutputStream>
+    static void Encode(OutputStream& os, unsigned codepoint) {
+        if (codepoint <= 0x7F) 
+            os.Put(static_cast<Ch>(codepoint & 0xFF));
+        else if (codepoint <= 0x7FF) {
+            os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+            os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+        }
+        else if (codepoint <= 0xFFFF) {
+            os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+            os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+            os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+        }
+        else {
+            RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+            os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+            os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+            os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+            os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+        }
+    }
+
+    template<typename OutputStream>
+    static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+        if (codepoint <= 0x7F) 
+            PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+        else if (codepoint <= 0x7FF) {
+            PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+            PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+        }
+        else if (codepoint <= 0xFFFF) {
+            PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+            PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+            PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+        }
+        else {
+            RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+            PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+            PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+            PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+            PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+        }
+    }
+
+    template <typename InputStream>
+    static bool Decode(InputStream& is, unsigned* codepoint) {
+#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
+#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
+#define TAIL() COPY(); TRANS(0x70)
+        typename InputStream::Ch c = is.Take();
+        if (!(c & 0x80)) {
+            *codepoint = static_cast<unsigned char>(c);
+            return true;
+        }
+
+        unsigned char type = GetRange(static_cast<unsigned char>(c));
+        if (type >= 32) {
+            *codepoint = 0;
+        } else {
+            *codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
+        }
+        bool result = true;
+        switch (type) {
+        case 2: TAIL(); return result;
+        case 3: TAIL(); TAIL(); return result;
+        case 4: COPY(); TRANS(0x50); TAIL(); return result;
+        case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
+        case 6: TAIL(); TAIL(); TAIL(); return result;
+        case 10: COPY(); TRANS(0x20); TAIL(); return result;
+        case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
+        default: return false;
+        }
+#undef COPY
+#undef TRANS
+#undef TAIL
+    }
+
+    template <typename InputStream, typename OutputStream>
+    static bool Validate(InputStream& is, OutputStream& os) {
+#define COPY() os.Put(c = is.Take())
+#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
+#define TAIL() COPY(); TRANS(0x70)
+        Ch c;
+        COPY();
+        if (!(c & 0x80))
+            return true;
+
+        bool result = true;
+        switch (GetRange(static_cast<unsigned char>(c))) {
+        case 2: TAIL(); return result;
+        case 3: TAIL(); TAIL(); return result;
+        case 4: COPY(); TRANS(0x50); TAIL(); return result;
+        case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
+        case 6: TAIL(); TAIL(); TAIL(); return result;
+        case 10: COPY(); TRANS(0x20); TAIL(); return result;
+        case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
+        default: return false;
+        }
+#undef COPY
+#undef TRANS
+#undef TAIL
+    }
+
+    static unsigned char GetRange(unsigned char c) {
+        // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+        // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
+        static const unsigned char type[] = {
+            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,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,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,
+            0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
+            0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
+            0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+            0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+            8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+            10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+        };
+        return type[c];
+    }
+
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        typename InputByteStream::Ch c = Take(is);
+        if (static_cast<unsigned char>(c) != 0xEFu) return c;
+        c = is.Take();
+        if (static_cast<unsigned char>(c) != 0xBBu) return c;
+        c = is.Take();
+        if (static_cast<unsigned char>(c) != 0xBFu) return c;
+        c = is.Take();
+        return c;
+    }
+
+    template <typename InputByteStream>
+    static Ch Take(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        return static_cast<Ch>(is.Take());
+    }
+
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
+    }
+
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, Ch c) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(c));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF16
+
+//! UTF-16 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-16
+    http://tools.ietf.org/html/rfc2781
+    \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
+    \note implements Encoding concept
+
+    \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
+    For streaming, use UTF16LE and UTF16BE, which handle endianness.
+*/
+template<typename CharType = wchar_t>
+struct UTF16 {
+    typedef CharType Ch;
+    RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
+
+    enum { supportUnicode = 1 };
+
+    template<typename OutputStream>
+    static void Encode(OutputStream& os, unsigned codepoint) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+        if (codepoint <= 0xFFFF) {
+            RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair 
+            os.Put(static_cast<typename OutputStream::Ch>(codepoint));
+        }
+        else {
+            RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+            unsigned v = codepoint - 0x10000;
+            os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+            os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
+        }
+    }
+
+
+    template<typename OutputStream>
+    static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+        if (codepoint <= 0xFFFF) {
+            RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair 
+            PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
+        }
+        else {
+            RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+            unsigned v = codepoint - 0x10000;
+            PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+            PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
+        }
+    }
+
+    template <typename InputStream>
+    static bool Decode(InputStream& is, unsigned* codepoint) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
+        typename InputStream::Ch c = is.Take();
+        if (c < 0xD800 || c > 0xDFFF) {
+            *codepoint = static_cast<unsigned>(c);
+            return true;
+        }
+        else if (c <= 0xDBFF) {
+            *codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
+            c = is.Take();
+            *codepoint |= (static_cast<unsigned>(c) & 0x3FF);
+            *codepoint += 0x10000;
+            return c >= 0xDC00 && c <= 0xDFFF;
+        }
+        return false;
+    }
+
+    template <typename InputStream, typename OutputStream>
+    static bool Validate(InputStream& is, OutputStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+        typename InputStream::Ch c;
+        os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
+        if (c < 0xD800 || c > 0xDFFF)
+            return true;
+        else if (c <= 0xDBFF) {
+            os.Put(c = is.Take());
+            return c >= 0xDC00 && c <= 0xDFFF;
+        }
+        return false;
+    }
+};
+
+//! UTF-16 little endian encoding.
+template<typename CharType = wchar_t>
+struct UTF16LE : UTF16<CharType> {
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        CharType c = Take(is);
+        return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
+    }
+
+    template <typename InputByteStream>
+    static CharType Take(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        unsigned c = static_cast<uint8_t>(is.Take());
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+        return static_cast<CharType>(c);
+    }
+
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+    }
+
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, CharType c) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
+    }
+};
+
+//! UTF-16 big endian encoding.
+template<typename CharType = wchar_t>
+struct UTF16BE : UTF16<CharType> {
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        CharType c = Take(is);
+        return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
+    }
+
+    template <typename InputByteStream>
+    static CharType Take(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
+        return static_cast<CharType>(c);
+    }
+
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+    }
+
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, CharType c) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF32
+
+//! UTF-32 encoding. 
+/*! http://en.wikipedia.org/wiki/UTF-32
+    \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
+    \note implements Encoding concept
+
+    \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
+    For streaming, use UTF32LE and UTF32BE, which handle endianness.
+*/
+template<typename CharType = unsigned>
+struct UTF32 {
+    typedef CharType Ch;
+    RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
+
+    enum { supportUnicode = 1 };
+
+    template<typename OutputStream>
+    static void Encode(OutputStream& os, unsigned codepoint) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+        RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+        os.Put(codepoint);
+    }
+
+    template<typename OutputStream>
+    static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+        RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+        PutUnsafe(os, codepoint);
+    }
+
+    template <typename InputStream>
+    static bool Decode(InputStream& is, unsigned* codepoint) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
+        Ch c = is.Take();
+        *codepoint = c;
+        return c <= 0x10FFFF;
+    }
+
+    template <typename InputStream, typename OutputStream>
+    static bool Validate(InputStream& is, OutputStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
+        Ch c;
+        os.Put(c = is.Take());
+        return c <= 0x10FFFF;
+    }
+};
+
+//! UTF-32 little endian enocoding.
+template<typename CharType = unsigned>
+struct UTF32LE : UTF32<CharType> {
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        CharType c = Take(is);
+        return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
+    }
+
+    template <typename InputByteStream>
+    static CharType Take(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        unsigned c = static_cast<uint8_t>(is.Take());
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
+        return static_cast<CharType>(c);
+    }
+
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+    }
+
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, CharType c) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
+    }
+};
+
+//! UTF-32 big endian encoding.
+template<typename CharType = unsigned>
+struct UTF32BE : UTF32<CharType> {
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        CharType c = Take(is);
+        return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c; 
+    }
+
+    template <typename InputByteStream>
+    static CharType Take(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+        c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
+        return static_cast<CharType>(c);
+    }
+
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+    }
+
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, CharType c) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
+        os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ASCII
+
+//! ASCII encoding.
+/*! http://en.wikipedia.org/wiki/ASCII
+    \tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
+    \note implements Encoding concept
+*/
+template<typename CharType = char>
+struct ASCII {
+    typedef CharType Ch;
+
+    enum { supportUnicode = 0 };
+
+    template<typename OutputStream>
+    static void Encode(OutputStream& os, unsigned codepoint) {
+        RAPIDJSON_ASSERT(codepoint <= 0x7F);
+        os.Put(static_cast<Ch>(codepoint & 0xFF));
+    }
+
+    template<typename OutputStream>
+    static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+        RAPIDJSON_ASSERT(codepoint <= 0x7F);
+        PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+    }
+
+    template <typename InputStream>
+    static bool Decode(InputStream& is, unsigned* codepoint) {
+        uint8_t c = static_cast<uint8_t>(is.Take());
+        *codepoint = c;
+        return c <= 0X7F;
+    }
+
+    template <typename InputStream, typename OutputStream>
+    static bool Validate(InputStream& is, OutputStream& os) {
+        uint8_t c = static_cast<uint8_t>(is.Take());
+        os.Put(static_cast<typename OutputStream::Ch>(c));
+        return c <= 0x7F;
+    }
+
+    template <typename InputByteStream>
+    static CharType TakeBOM(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        uint8_t c = static_cast<uint8_t>(Take(is));
+        return static_cast<Ch>(c);
+    }
+
+    template <typename InputByteStream>
+    static Ch Take(InputByteStream& is) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+        return static_cast<Ch>(is.Take());
+    }
+
+    template <typename OutputByteStream>
+    static void PutBOM(OutputByteStream& os) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        (void)os;
+    }
+
+    template <typename OutputByteStream>
+    static void Put(OutputByteStream& os, Ch c) {
+        RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+        os.Put(static_cast<typename OutputByteStream::Ch>(c));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// AutoUTF
+
+//! Runtime-specified UTF encoding type of a stream.
+enum UTFType {
+    kUTF8 = 0,      //!< UTF-8.
+    kUTF16LE = 1,   //!< UTF-16 little endian.
+    kUTF16BE = 2,   //!< UTF-16 big endian.
+    kUTF32LE = 3,   //!< UTF-32 little endian.
+    kUTF32BE = 4    //!< UTF-32 big endian.
+};
+
+//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
+/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
+*/
+template<typename CharType>
+struct AutoUTF {
+    typedef CharType Ch;
+
+    enum { supportUnicode = 1 };
+
+#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
+
+    template<typename OutputStream>
+    static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) {
+        typedef void (*EncodeFunc)(OutputStream&, unsigned);
+        static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
+        (*f[os.GetType()])(os, codepoint);
+    }
+
+    template<typename OutputStream>
+    static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+        typedef void (*EncodeFunc)(OutputStream&, unsigned);
+        static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
+        (*f[os.GetType()])(os, codepoint);
+    }
+
+    template <typename InputStream>
+    static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) {
+        typedef bool (*DecodeFunc)(InputStream&, unsigned*);
+        static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
+        return (*f[is.GetType()])(is, codepoint);
+    }
+
+    template <typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
+        typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
+        static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
+        return (*f[is.GetType()])(is, os);
+    }
+
+#undef RAPIDJSON_ENCODINGS_FUNC
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Transcoder
+
+//! Encoding conversion.
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder {
+    //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
+        unsigned codepoint;
+        if (!SourceEncoding::Decode(is, &codepoint))
+            return false;
+        TargetEncoding::Encode(os, codepoint);
+        return true;
+    }
+
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+        unsigned codepoint;
+        if (!SourceEncoding::Decode(is, &codepoint))
+            return false;
+        TargetEncoding::EncodeUnsafe(os, codepoint);
+        return true;
+    }
+
+    //! Validate one Unicode codepoint from an encoded stream.
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
+        return Transcode(is, os);   // Since source/target encoding is different, must transcode.
+    }
+};
+
+// Forward declaration.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
+
+//! Specialization of Transcoder with same source and target encoding.
+template<typename Encoding>
+struct Transcoder<Encoding, Encoding> {
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
+        os.Put(is.Take());  // Just copy one code unit. This semantic is different from primary template class.
+        return true;
+    }
+    
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+        PutUnsafe(os, is.Take());  // Just copy one code unit. This semantic is different from primary template class.
+        return true;
+    }
+    
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
+        return Encoding::Validate(is, os);  // source/target encoding are the same
+    }
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/src/lottie/rapidjson/error/en.h b/src/lottie/rapidjson/error/en.h
new file mode 100644 (file)
index 0000000..2db838b
--- /dev/null
@@ -0,0 +1,74 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ERROR_EN_H_
+#define RAPIDJSON_ERROR_EN_H_
+
+#include "error.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(covered-switch-default)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Maps error code of parsing into error message.
+/*!
+    \ingroup RAPIDJSON_ERRORS
+    \param parseErrorCode Error code obtained in parsing.
+    \return the error message.
+    \note User can make a copy of this function for localization.
+        Using switch-case is safer for future modification of error codes.
+*/
+inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
+    switch (parseErrorCode) {
+        case kParseErrorNone:                           return RAPIDJSON_ERROR_STRING("No error.");
+
+        case kParseErrorDocumentEmpty:                  return RAPIDJSON_ERROR_STRING("The document is empty.");
+        case kParseErrorDocumentRootNotSingular:        return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
+    
+        case kParseErrorValueInvalid:                   return RAPIDJSON_ERROR_STRING("Invalid value.");
+    
+        case kParseErrorObjectMissName:                 return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
+        case kParseErrorObjectMissColon:                return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
+        case kParseErrorObjectMissCommaOrCurlyBracket:  return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
+    
+        case kParseErrorArrayMissCommaOrSquareBracket:  return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
+
+        case kParseErrorStringUnicodeEscapeInvalidHex:  return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
+        case kParseErrorStringUnicodeSurrogateInvalid:  return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
+        case kParseErrorStringEscapeInvalid:            return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
+        case kParseErrorStringMissQuotationMark:        return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
+        case kParseErrorStringInvalidEncoding:          return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
+
+        case kParseErrorNumberTooBig:                   return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
+        case kParseErrorNumberMissFraction:             return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
+        case kParseErrorNumberMissExponent:             return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
+
+        case kParseErrorTermination:                    return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
+        case kParseErrorUnspecificSyntaxError:          return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
+
+        default:                                        return RAPIDJSON_ERROR_STRING("Unknown error.");
+    }
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ERROR_EN_H_
diff --git a/src/lottie/rapidjson/error/error.h b/src/lottie/rapidjson/error/error.h
new file mode 100644 (file)
index 0000000..9311d2f
--- /dev/null
@@ -0,0 +1,161 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ERROR_ERROR_H_
+#define RAPIDJSON_ERROR_ERROR_H_
+
+#include "../rapidjson.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+/*! \file error.h */
+
+/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ERROR_CHARTYPE
+
+//! Character type of error messages.
+/*! \ingroup RAPIDJSON_ERRORS
+    The default character type is \c char.
+    On Windows, user can define this macro as \c TCHAR for supporting both
+    unicode/non-unicode settings.
+*/
+#ifndef RAPIDJSON_ERROR_CHARTYPE
+#define RAPIDJSON_ERROR_CHARTYPE char
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ERROR_STRING
+
+//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
+/*! \ingroup RAPIDJSON_ERRORS
+    By default this conversion macro does nothing.
+    On Windows, user can define this macro as \c _T(x) for supporting both
+    unicode/non-unicode settings.
+*/
+#ifndef RAPIDJSON_ERROR_STRING
+#define RAPIDJSON_ERROR_STRING(x) x
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// ParseErrorCode
+
+//! Error code of parsing.
+/*! \ingroup RAPIDJSON_ERRORS
+    \see GenericReader::Parse, GenericReader::GetParseErrorCode
+*/
+enum ParseErrorCode {
+    kParseErrorNone = 0,                        //!< No error.
+
+    kParseErrorDocumentEmpty,                   //!< The document is empty.
+    kParseErrorDocumentRootNotSingular,         //!< The document root must not follow by other values.
+
+    kParseErrorValueInvalid,                    //!< Invalid value.
+
+    kParseErrorObjectMissName,                  //!< Missing a name for object member.
+    kParseErrorObjectMissColon,                 //!< Missing a colon after a name of object member.
+    kParseErrorObjectMissCommaOrCurlyBracket,   //!< Missing a comma or '}' after an object member.
+
+    kParseErrorArrayMissCommaOrSquareBracket,   //!< Missing a comma or ']' after an array element.
+
+    kParseErrorStringUnicodeEscapeInvalidHex,   //!< Incorrect hex digit after \\u escape in string.
+    kParseErrorStringUnicodeSurrogateInvalid,   //!< The surrogate pair in string is invalid.
+    kParseErrorStringEscapeInvalid,             //!< Invalid escape character in string.
+    kParseErrorStringMissQuotationMark,         //!< Missing a closing quotation mark in string.
+    kParseErrorStringInvalidEncoding,           //!< Invalid encoding in string.
+
+    kParseErrorNumberTooBig,                    //!< Number too big to be stored in double.
+    kParseErrorNumberMissFraction,              //!< Miss fraction part in number.
+    kParseErrorNumberMissExponent,              //!< Miss exponent in number.
+
+    kParseErrorTermination,                     //!< Parsing was terminated.
+    kParseErrorUnspecificSyntaxError            //!< Unspecific syntax error.
+};
+
+//! Result of parsing (wraps ParseErrorCode)
+/*!
+    \ingroup RAPIDJSON_ERRORS
+    \code
+        Document doc;
+        ParseResult ok = doc.Parse("[42]");
+        if (!ok) {
+            fprintf(stderr, "JSON parse error: %s (%u)",
+                    GetParseError_En(ok.Code()), ok.Offset());
+            exit(EXIT_FAILURE);
+        }
+    \endcode
+    \see GenericReader::Parse, GenericDocument::Parse
+*/
+struct ParseResult {
+    //!! Unspecified boolean type
+    typedef bool (ParseResult::*BooleanType)() const;
+public:
+    //! Default constructor, no error.
+    ParseResult() : code_(kParseErrorNone), offset_(0) {}
+    //! Constructor to set an error.
+    ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
+
+    //! Get the error code.
+    ParseErrorCode Code() const { return code_; }
+    //! Get the error offset, if \ref IsError(), 0 otherwise.
+    size_t Offset() const { return offset_; }
+
+    //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError().
+    operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; }
+    //! Whether the result is an error.
+    bool IsError() const { return code_ != kParseErrorNone; }
+
+    bool operator==(const ParseResult& that) const { return code_ == that.code_; }
+    bool operator==(ParseErrorCode code) const { return code_ == code; }
+    friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
+
+    bool operator!=(const ParseResult& that) const { return !(*this == that); }
+    bool operator!=(ParseErrorCode code) const { return !(*this == code); }
+    friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; }
+
+    //! Reset error code.
+    void Clear() { Set(kParseErrorNone); }
+    //! Update error code and offset.
+    void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
+
+private:
+    ParseErrorCode code_;
+    size_t offset_;
+};
+
+//! Function pointer type of GetParseError().
+/*! \ingroup RAPIDJSON_ERRORS
+
+    This is the prototype for \c GetParseError_X(), where \c X is a locale.
+    User can dynamically change locale in runtime, e.g.:
+\code
+    GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
+    const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
+\endcode
+*/
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ERROR_ERROR_H_
diff --git a/src/lottie/rapidjson/filereadstream.h b/src/lottie/rapidjson/filereadstream.h
new file mode 100644 (file)
index 0000000..f1bfb7d
--- /dev/null
@@ -0,0 +1,99 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FILEREADSTREAM_H_
+#define RAPIDJSON_FILEREADSTREAM_H_
+
+#include "stream.h"
+#include <cstdio>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! File byte stream for input using fread().
+/*!
+    \note implements Stream concept
+*/
+class FileReadStream {
+public:
+    typedef char Ch;    //!< Character type (byte).
+
+    //! Constructor.
+    /*!
+        \param fp File pointer opened for read.
+        \param buffer user-supplied buffer.
+        \param bufferSize size of buffer in bytes. Must >=4 bytes.
+    */
+    FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { 
+        RAPIDJSON_ASSERT(fp_ != 0);
+        RAPIDJSON_ASSERT(bufferSize >= 4);
+        Read();
+    }
+
+    Ch Peek() const { return *current_; }
+    Ch Take() { Ch c = *current_; Read(); return c; }
+    size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
+
+    // Not implemented
+    void Put(Ch) { RAPIDJSON_ASSERT(false); }
+    void Flush() { RAPIDJSON_ASSERT(false); } 
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+    // For encoding detection only.
+    const Ch* Peek4() const {
+        return (current_ + 4 <= bufferLast_) ? current_ : 0;
+    }
+
+private:
+    void Read() {
+        if (current_ < bufferLast_)
+            ++current_;
+        else if (!eof_) {
+            count_ += readCount_;
+            readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
+            bufferLast_ = buffer_ + readCount_ - 1;
+            current_ = buffer_;
+
+            if (readCount_ < bufferSize_) {
+                buffer_[readCount_] = '\0';
+                ++bufferLast_;
+                eof_ = true;
+            }
+        }
+    }
+
+    std::FILE* fp_;
+    Ch *buffer_;
+    size_t bufferSize_;
+    Ch *bufferLast_;
+    Ch *current_;
+    size_t readCount_;
+    size_t count_;  //!< Number of characters read
+    bool eof_;
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/src/lottie/rapidjson/filewritestream.h b/src/lottie/rapidjson/filewritestream.h
new file mode 100644 (file)
index 0000000..3811f8b
--- /dev/null
@@ -0,0 +1,104 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FILEWRITESTREAM_H_
+#define RAPIDJSON_FILEWRITESTREAM_H_
+
+#include "stream.h"
+#include <cstdio>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of C file stream for input using fread().
+/*!
+    \note implements Stream concept
+*/
+class FileWriteStream {
+public:
+    typedef char Ch;    //!< Character type. Only support char.
+
+    FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { 
+        RAPIDJSON_ASSERT(fp_ != 0);
+    }
+
+    void Put(char c) { 
+        if (current_ >= bufferEnd_)
+            Flush();
+
+        *current_++ = c;
+    }
+
+    void PutN(char c, size_t n) {
+        size_t avail = static_cast<size_t>(bufferEnd_ - current_);
+        while (n > avail) {
+            std::memset(current_, c, avail);
+            current_ += avail;
+            Flush();
+            n -= avail;
+            avail = static_cast<size_t>(bufferEnd_ - current_);
+        }
+
+        if (n > 0) {
+            std::memset(current_, c, n);
+            current_ += n;
+        }
+    }
+
+    void Flush() {
+        if (current_ != buffer_) {
+            size_t result = std::fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
+            if (result < static_cast<size_t>(current_ - buffer_)) {
+                // failure deliberately ignored at this time
+                // added to avoid warn_unused_result build errors
+            }
+            current_ = buffer_;
+        }
+    }
+
+    // Not implemented
+    char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+    char Take() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+    char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+    // Prohibit copy constructor & assignment operator.
+    FileWriteStream(const FileWriteStream&);
+    FileWriteStream& operator=(const FileWriteStream&);
+
+    std::FILE* fp_;
+    char *buffer_;
+    char *bufferEnd_;
+    char *current_;
+};
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(FileWriteStream& stream, char c, size_t n) {
+    stream.PutN(c, n);
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/src/lottie/rapidjson/fwd.h b/src/lottie/rapidjson/fwd.h
new file mode 100644 (file)
index 0000000..e8104e8
--- /dev/null
@@ -0,0 +1,151 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FWD_H_
+#define RAPIDJSON_FWD_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// encodings.h
+
+template<typename CharType> struct UTF8;
+template<typename CharType> struct UTF16;
+template<typename CharType> struct UTF16BE;
+template<typename CharType> struct UTF16LE;
+template<typename CharType> struct UTF32;
+template<typename CharType> struct UTF32BE;
+template<typename CharType> struct UTF32LE;
+template<typename CharType> struct ASCII;
+template<typename CharType> struct AutoUTF;
+
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder;
+
+// allocators.h
+
+class CrtAllocator;
+
+template <typename BaseAllocator>
+class MemoryPoolAllocator;
+
+// stream.h
+
+template <typename Encoding>
+struct GenericStringStream;
+
+typedef GenericStringStream<UTF8<char> > StringStream;
+
+template <typename Encoding>
+struct GenericInsituStringStream;
+
+typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
+
+// stringbuffer.h
+
+template <typename Encoding, typename Allocator>
+class GenericStringBuffer;
+
+typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
+
+// filereadstream.h
+
+class FileReadStream;
+
+// filewritestream.h
+
+class FileWriteStream;
+
+// memorybuffer.h
+
+template <typename Allocator>
+struct GenericMemoryBuffer;
+
+typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
+
+// memorystream.h
+
+struct MemoryStream;
+
+// reader.h
+
+template<typename Encoding, typename Derived>
+struct BaseReaderHandler;
+
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
+class GenericReader;
+
+typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
+
+// writer.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class Writer;
+
+// prettywriter.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class PrettyWriter;
+
+// document.h
+
+template <typename Encoding, typename Allocator> 
+struct GenericMember;
+
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator;
+
+template<typename CharType>
+struct GenericStringRef;
+
+template <typename Encoding, typename Allocator> 
+class GenericValue;
+
+typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
+
+// pointer.h
+
+template <typename ValueType, typename Allocator>
+class GenericPointer;
+
+typedef GenericPointer<Value, CrtAllocator> Pointer;
+
+// schema.h
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider;
+
+template <typename ValueT, typename Allocator>
+class GenericSchemaDocument;
+
+typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+template <
+    typename SchemaDocumentType,
+    typename OutputHandler,
+    typename StateAllocator>
+class GenericSchemaValidator;
+
+typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSONFWD_H_
diff --git a/src/lottie/rapidjson/internal/biginteger.h b/src/lottie/rapidjson/internal/biginteger.h
new file mode 100644 (file)
index 0000000..9d3e88c
--- /dev/null
@@ -0,0 +1,290 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_BIGINTEGER_H_
+#define RAPIDJSON_BIGINTEGER_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && defined(_M_AMD64)
+#include <intrin.h> // for _umul128
+#pragma intrinsic(_umul128)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+class BigInteger {
+public:
+    typedef uint64_t Type;
+
+    BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
+        std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
+    }
+
+    explicit BigInteger(uint64_t u) : count_(1) {
+        digits_[0] = u;
+    }
+
+    BigInteger(const char* decimals, size_t length) : count_(1) {
+        RAPIDJSON_ASSERT(length > 0);
+        digits_[0] = 0;
+        size_t i = 0;
+        const size_t kMaxDigitPerIteration = 19;  // 2^64 = 18446744073709551616 > 10^19
+        while (length >= kMaxDigitPerIteration) {
+            AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
+            length -= kMaxDigitPerIteration;
+            i += kMaxDigitPerIteration;
+        }
+
+        if (length > 0)
+            AppendDecimal64(decimals + i, decimals + i + length);
+    }
+    
+    BigInteger& operator=(const BigInteger &rhs)
+    {
+        if (this != &rhs) {
+            count_ = rhs.count_;
+            std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
+        }
+        return *this;
+    }
+    
+    BigInteger& operator=(uint64_t u) {
+        digits_[0] = u;            
+        count_ = 1;
+        return *this;
+    }
+
+    BigInteger& operator+=(uint64_t u) {
+        Type backup = digits_[0];
+        digits_[0] += u;
+        for (size_t i = 0; i < count_ - 1; i++) {
+            if (digits_[i] >= backup)
+                return *this; // no carry
+            backup = digits_[i + 1];
+            digits_[i + 1] += 1;
+        }
+
+        // Last carry
+        if (digits_[count_ - 1] < backup)
+            PushBack(1);
+
+        return *this;
+    }
+
+    BigInteger& operator*=(uint64_t u) {
+        if (u == 0) return *this = 0;
+        if (u == 1) return *this;
+        if (*this == 1) return *this = u;
+
+        uint64_t k = 0;
+        for (size_t i = 0; i < count_; i++) {
+            uint64_t hi;
+            digits_[i] = MulAdd64(digits_[i], u, k, &hi);
+            k = hi;
+        }
+        
+        if (k > 0)
+            PushBack(k);
+
+        return *this;
+    }
+
+    BigInteger& operator*=(uint32_t u) {
+        if (u == 0) return *this = 0;
+        if (u == 1) return *this;
+        if (*this == 1) return *this = u;
+
+        uint64_t k = 0;
+        for (size_t i = 0; i < count_; i++) {
+            const uint64_t c = digits_[i] >> 32;
+            const uint64_t d = digits_[i] & 0xFFFFFFFF;
+            const uint64_t uc = u * c;
+            const uint64_t ud = u * d;
+            const uint64_t p0 = ud + k;
+            const uint64_t p1 = uc + (p0 >> 32);
+            digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
+            k = p1 >> 32;
+        }
+        
+        if (k > 0)
+            PushBack(k);
+
+        return *this;
+    }
+
+    BigInteger& operator<<=(size_t shift) {
+        if (IsZero() || shift == 0) return *this;
+
+        size_t offset = shift / kTypeBit;
+        size_t interShift = shift % kTypeBit;
+        RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
+
+        if (interShift == 0) {
+            std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
+            count_ += offset;
+        }
+        else {
+            digits_[count_] = 0;
+            for (size_t i = count_; i > 0; i--)
+                digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
+            digits_[offset] = digits_[0] << interShift;
+            count_ += offset;
+            if (digits_[count_])
+                count_++;
+        }
+
+        std::memset(digits_, 0, offset * sizeof(Type));
+
+        return *this;
+    }
+
+    bool operator==(const BigInteger& rhs) const {
+        return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
+    }
+
+    bool operator==(const Type rhs) const {
+        return count_ == 1 && digits_[0] == rhs;
+    }
+
+    BigInteger& MultiplyPow5(unsigned exp) {
+        static const uint32_t kPow5[12] = {
+            5,
+            5 * 5,
+            5 * 5 * 5,
+            5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
+        };
+        if (exp == 0) return *this;
+        for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
+        for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
+        if (exp > 0)                 *this *= kPow5[exp - 1];
+        return *this;
+    }
+
+    // Compute absolute difference of this and rhs.
+    // Assume this != rhs
+    bool Difference(const BigInteger& rhs, BigInteger* out) const {
+        int cmp = Compare(rhs);
+        RAPIDJSON_ASSERT(cmp != 0);
+        const BigInteger *a, *b;  // Makes a > b
+        bool ret;
+        if (cmp < 0) { a = &rhs; b = this; ret = true; }
+        else         { a = this; b = &rhs; ret = false; }
+
+        Type borrow = 0;
+        for (size_t i = 0; i < a->count_; i++) {
+            Type d = a->digits_[i] - borrow;
+            if (i < b->count_)
+                d -= b->digits_[i];
+            borrow = (d > a->digits_[i]) ? 1 : 0;
+            out->digits_[i] = d;
+            if (d != 0)
+                out->count_ = i + 1;
+        }
+
+        return ret;
+    }
+
+    int Compare(const BigInteger& rhs) const {
+        if (count_ != rhs.count_)
+            return count_ < rhs.count_ ? -1 : 1;
+
+        for (size_t i = count_; i-- > 0;)
+            if (digits_[i] != rhs.digits_[i])
+                return digits_[i] < rhs.digits_[i] ? -1 : 1;
+
+        return 0;
+    }
+
+    size_t GetCount() const { return count_; }
+    Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
+    bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
+
+private:
+    void AppendDecimal64(const char* begin, const char* end) {
+        uint64_t u = ParseUint64(begin, end);
+        if (IsZero())
+            *this = u;
+        else {
+            unsigned exp = static_cast<unsigned>(end - begin);
+            (MultiplyPow5(exp) <<= exp) += u;   // *this = *this * 10^exp + u
+        }
+    }
+
+    void PushBack(Type digit) {
+        RAPIDJSON_ASSERT(count_ < kCapacity);
+        digits_[count_++] = digit;
+    }
+
+    static uint64_t ParseUint64(const char* begin, const char* end) {
+        uint64_t r = 0;
+        for (const char* p = begin; p != end; ++p) {
+            RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
+            r = r * 10u + static_cast<unsigned>(*p - '0');
+        }
+        return r;
+    }
+
+    // Assume a * b + k < 2^128
+    static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+        uint64_t low = _umul128(a, b, outHigh) + k;
+        if (low < k)
+            (*outHigh)++;
+        return low;
+#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
+        __extension__ typedef unsigned __int128 uint128;
+        uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
+        p += k;
+        *outHigh = static_cast<uint64_t>(p >> 64);
+        return static_cast<uint64_t>(p);
+#else
+        const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
+        uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
+        x1 += (x0 >> 32); // can't give carry
+        x1 += x2;
+        if (x1 < x2)
+            x3 += (static_cast<uint64_t>(1) << 32);
+        uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
+        uint64_t hi = x3 + (x1 >> 32);
+
+        lo += k;
+        if (lo < k)
+            hi++;
+        *outHigh = hi;
+        return lo;
+#endif
+    }
+
+    static const size_t kBitCount = 3328;  // 64bit * 54 > 10^1000
+    static const size_t kCapacity = kBitCount / sizeof(Type);
+    static const size_t kTypeBit = sizeof(Type) * 8;
+
+    Type digits_[kCapacity];
+    size_t count_;
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_BIGINTEGER_H_
diff --git a/src/lottie/rapidjson/internal/diyfp.h b/src/lottie/rapidjson/internal/diyfp.h
new file mode 100644 (file)
index 0000000..29abf80
--- /dev/null
@@ -0,0 +1,258 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
+// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
+// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+
+#ifndef RAPIDJSON_DIYFP_H_
+#define RAPIDJSON_DIYFP_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse64)
+#pragma intrinsic(_umul128)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+struct DiyFp {
+    DiyFp() : f(), e() {}
+
+    DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
+
+    explicit DiyFp(double d) {
+        union {
+            double d;
+            uint64_t u64;
+        } u = { d };
+
+        int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
+        uint64_t significand = (u.u64 & kDpSignificandMask);
+        if (biased_e != 0) {
+            f = significand + kDpHiddenBit;
+            e = biased_e - kDpExponentBias;
+        } 
+        else {
+            f = significand;
+            e = kDpMinExponent + 1;
+        }
+    }
+
+    DiyFp operator-(const DiyFp& rhs) const {
+        return DiyFp(f - rhs.f, e);
+    }
+
+    DiyFp operator*(const DiyFp& rhs) const {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+        uint64_t h;
+        uint64_t l = _umul128(f, rhs.f, &h);
+        if (l & (uint64_t(1) << 63)) // rounding
+            h++;
+        return DiyFp(h, e + rhs.e + 64);
+#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
+        __extension__ typedef unsigned __int128 uint128;
+        uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
+        uint64_t h = static_cast<uint64_t>(p >> 64);
+        uint64_t l = static_cast<uint64_t>(p);
+        if (l & (uint64_t(1) << 63)) // rounding
+            h++;
+        return DiyFp(h, e + rhs.e + 64);
+#else
+        const uint64_t M32 = 0xFFFFFFFF;
+        const uint64_t a = f >> 32;
+        const uint64_t b = f & M32;
+        const uint64_t c = rhs.f >> 32;
+        const uint64_t d = rhs.f & M32;
+        const uint64_t ac = a * c;
+        const uint64_t bc = b * c;
+        const uint64_t ad = a * d;
+        const uint64_t bd = b * d;
+        uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
+        tmp += 1U << 31;  /// mult_round
+        return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
+#endif
+    }
+
+    DiyFp Normalize() const {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+        unsigned long index;
+        _BitScanReverse64(&index, f);
+        return DiyFp(f << (63 - index), e - (63 - index));
+#elif defined(__GNUC__) && __GNUC__ >= 4
+        int s = __builtin_clzll(f);
+        return DiyFp(f << s, e - s);
+#else
+        DiyFp res = *this;
+        while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
+            res.f <<= 1;
+            res.e--;
+        }
+        return res;
+#endif
+    }
+
+    DiyFp NormalizeBoundary() const {
+        DiyFp res = *this;
+        while (!(res.f & (kDpHiddenBit << 1))) {
+            res.f <<= 1;
+            res.e--;
+        }
+        res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
+        res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
+        return res;
+    }
+
+    void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
+        DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
+        DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
+        mi.f <<= mi.e - pl.e;
+        mi.e = pl.e;
+        *plus = pl;
+        *minus = mi;
+    }
+
+    double ToDouble() const {
+        union {
+            double d;
+            uint64_t u64;
+        }u;
+        const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : 
+            static_cast<uint64_t>(e + kDpExponentBias);
+        u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
+        return u.d;
+    }
+
+    static const int kDiySignificandSize = 64;
+    static const int kDpSignificandSize = 52;
+    static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
+    static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
+    static const int kDpMinExponent = -kDpExponentBias;
+    static const int kDpDenormalExponent = -kDpExponentBias + 1;
+    static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+    static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+    static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+    uint64_t f;
+    int e;
+};
+
+inline DiyFp GetCachedPowerByIndex(size_t index) {
+    // 10^-348, 10^-340, ..., 10^340
+    static const uint64_t kCachedPowers_F[] = {
+        RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
+        RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
+        RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
+        RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
+        RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
+        RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
+        RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
+        RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
+        RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
+        RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
+        RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
+        RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
+        RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
+        RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
+        RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
+        RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
+        RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
+        RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
+        RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
+        RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
+        RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
+        RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
+        RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
+        RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
+        RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
+        RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
+        RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
+        RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
+        RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
+        RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
+        RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
+        RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
+        RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
+        RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
+        RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
+        RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
+        RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
+        RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
+        RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
+        RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
+        RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
+        RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
+        RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
+        RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
+    };
+    static const int16_t kCachedPowers_E[] = {
+        -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007,  -980,
+        -954,  -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,
+        -688,  -661,  -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,
+        -422,  -396,  -369,  -343,  -316,  -289,  -263,  -236,  -210,  -183,
+        -157,  -130,  -103,   -77,   -50,   -24,     3,    30,    56,    83,
+        109,   136,   162,   189,   216,   242,   269,   295,   322,   348,
+        375,   402,   428,   455,   481,   508,   534,   561,   588,   614,
+        641,   667,   694,   720,   747,   774,   800,   827,   853,   880,
+        907,   933,   960,   986,  1013,  1039,  1066
+    };
+    return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
+}
+    
+inline DiyFp GetCachedPower(int e, int* K) {
+
+    //int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
+    double dk = (-61 - e) * 0.30102999566398114 + 347;  // dk must be positive, so can do ceiling in positive
+    int k = static_cast<int>(dk);
+    if (dk - k > 0.0)
+        k++;
+
+    unsigned index = static_cast<unsigned>((k >> 3) + 1);
+    *K = -(-348 + static_cast<int>(index << 3));    // decimal exponent no need lookup table
+
+    return GetCachedPowerByIndex(index);
+}
+
+inline DiyFp GetCachedPower10(int exp, int *outExp) {
+     unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
+     *outExp = -348 + static_cast<int>(index) * 8;
+     return GetCachedPowerByIndex(index);
+ }
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_DIYFP_H_
diff --git a/src/lottie/rapidjson/internal/dtoa.h b/src/lottie/rapidjson/internal/dtoa.h
new file mode 100644 (file)
index 0000000..bf2e9b2
--- /dev/null
@@ -0,0 +1,245 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
+// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
+// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+
+#ifndef RAPIDJSON_DTOA_
+#define RAPIDJSON_DTOA_
+
+#include "itoa.h" // GetDigitsLut()
+#include "diyfp.h"
+#include "ieee754.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
+#endif
+
+inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
+    while (rest < wp_w && delta - rest >= ten_kappa &&
+           (rest + ten_kappa < wp_w ||  /// closer
+            wp_w - rest > rest + ten_kappa - wp_w)) {
+        buffer[len - 1]--;
+        rest += ten_kappa;
+    }
+}
+
+inline int CountDecimalDigit32(uint32_t n) {
+    // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
+    if (n < 10) return 1;
+    if (n < 100) return 2;
+    if (n < 1000) return 3;
+    if (n < 10000) return 4;
+    if (n < 100000) return 5;
+    if (n < 1000000) return 6;
+    if (n < 10000000) return 7;
+    if (n < 100000000) return 8;
+    // Will not reach 10 digits in DigitGen()
+    //if (n < 1000000000) return 9;
+    //return 10;
+    return 9;
+}
+
+inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
+    static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+    const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
+    const DiyFp wp_w = Mp - W;
+    uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
+    uint64_t p2 = Mp.f & (one.f - 1);
+    int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
+    *len = 0;
+
+    while (kappa > 0) {
+        uint32_t d = 0;
+        switch (kappa) {
+            case  9: d = p1 /  100000000; p1 %=  100000000; break;
+            case  8: d = p1 /   10000000; p1 %=   10000000; break;
+            case  7: d = p1 /    1000000; p1 %=    1000000; break;
+            case  6: d = p1 /     100000; p1 %=     100000; break;
+            case  5: d = p1 /      10000; p1 %=      10000; break;
+            case  4: d = p1 /       1000; p1 %=       1000; break;
+            case  3: d = p1 /        100; p1 %=        100; break;
+            case  2: d = p1 /         10; p1 %=         10; break;
+            case  1: d = p1;              p1 =           0; break;
+            default:;
+        }
+        if (d || *len)
+            buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
+        kappa--;
+        uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
+        if (tmp <= delta) {
+            *K += kappa;
+            GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
+            return;
+        }
+    }
+
+    // kappa = 0
+    for (;;) {
+        p2 *= 10;
+        delta *= 10;
+        char d = static_cast<char>(p2 >> -one.e);
+        if (d || *len)
+            buffer[(*len)++] = static_cast<char>('0' + d);
+        p2 &= one.f - 1;
+        kappa--;
+        if (p2 < delta) {
+            *K += kappa;
+            int index = -kappa;
+            GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0));
+            return;
+        }
+    }
+}
+
+inline void Grisu2(double value, char* buffer, int* length, int* K) {
+    const DiyFp v(value);
+    DiyFp w_m, w_p;
+    v.NormalizedBoundaries(&w_m, &w_p);
+
+    const DiyFp c_mk = GetCachedPower(w_p.e, K);
+    const DiyFp W = v.Normalize() * c_mk;
+    DiyFp Wp = w_p * c_mk;
+    DiyFp Wm = w_m * c_mk;
+    Wm.f++;
+    Wp.f--;
+    DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
+}
+
+inline char* WriteExponent(int K, char* buffer) {
+    if (K < 0) {
+        *buffer++ = '-';
+        K = -K;
+    }
+
+    if (K >= 100) {
+        *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
+        K %= 100;
+        const char* d = GetDigitsLut() + K * 2;
+        *buffer++ = d[0];
+        *buffer++ = d[1];
+    }
+    else if (K >= 10) {
+        const char* d = GetDigitsLut() + K * 2;
+        *buffer++ = d[0];
+        *buffer++ = d[1];
+    }
+    else
+        *buffer++ = static_cast<char>('0' + static_cast<char>(K));
+
+    return buffer;
+}
+
+inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
+    const int kk = length + k;  // 10^(kk-1) <= v < 10^kk
+
+    if (0 <= k && kk <= 21) {
+        // 1234e7 -> 12340000000
+        for (int i = length; i < kk; i++)
+            buffer[i] = '0';
+        buffer[kk] = '.';
+        buffer[kk + 1] = '0';
+        return &buffer[kk + 2];
+    }
+    else if (0 < kk && kk <= 21) {
+        // 1234e-2 -> 12.34
+        std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
+        buffer[kk] = '.';
+        if (0 > k + maxDecimalPlaces) {
+            // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
+            // Remove extra trailing zeros (at least one) after truncation.
+            for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
+                if (buffer[i] != '0')
+                    return &buffer[i + 1];
+            return &buffer[kk + 2]; // Reserve one zero
+        }
+        else
+            return &buffer[length + 1];
+    }
+    else if (-6 < kk && kk <= 0) {
+        // 1234e-6 -> 0.001234
+        const int offset = 2 - kk;
+        std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
+        buffer[0] = '0';
+        buffer[1] = '.';
+        for (int i = 2; i < offset; i++)
+            buffer[i] = '0';
+        if (length - kk > maxDecimalPlaces) {
+            // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
+            // Remove extra trailing zeros (at least one) after truncation.
+            for (int i = maxDecimalPlaces + 1; i > 2; i--)
+                if (buffer[i] != '0')
+                    return &buffer[i + 1];
+            return &buffer[3]; // Reserve one zero
+        }
+        else
+            return &buffer[length + offset];
+    }
+    else if (kk < -maxDecimalPlaces) {
+        // Truncate to zero
+        buffer[0] = '0';
+        buffer[1] = '.';
+        buffer[2] = '0';
+        return &buffer[3];
+    }
+    else if (length == 1) {
+        // 1e30
+        buffer[1] = 'e';
+        return WriteExponent(kk - 1, &buffer[2]);
+    }
+    else {
+        // 1234e30 -> 1.234e33
+        std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
+        buffer[1] = '.';
+        buffer[length + 1] = 'e';
+        return WriteExponent(kk - 1, &buffer[0 + length + 2]);
+    }
+}
+
+inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
+    RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
+    Double d(value);
+    if (d.IsZero()) {
+        if (d.Sign())
+            *buffer++ = '-';     // -0.0, Issue #289
+        buffer[0] = '0';
+        buffer[1] = '.';
+        buffer[2] = '0';
+        return &buffer[3];
+    }
+    else {
+        if (value < 0) {
+            *buffer++ = '-';
+            value = -value;
+        }
+        int length, K;
+        Grisu2(value, buffer, &length, &K);
+        return Prettify(buffer, length, K, maxDecimalPlaces);
+    }
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_DTOA_
diff --git a/src/lottie/rapidjson/internal/ieee754.h b/src/lottie/rapidjson/internal/ieee754.h
new file mode 100644 (file)
index 0000000..c2684ba
--- /dev/null
@@ -0,0 +1,78 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_IEEE754_
+#define RAPIDJSON_IEEE754_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+class Double {
+public:
+    Double() {}
+    Double(double d) : d_(d) {}
+    Double(uint64_t u) : u_(u) {}
+
+    double Value() const { return d_; }
+    uint64_t Uint64Value() const { return u_; }
+
+    double NextPositiveDouble() const {
+        RAPIDJSON_ASSERT(!Sign());
+        return Double(u_ + 1).Value();
+    }
+
+    bool Sign() const { return (u_ & kSignMask) != 0; }
+    uint64_t Significand() const { return u_ & kSignificandMask; }
+    int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
+
+    bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
+    bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
+    bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
+    bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
+    bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
+
+    uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
+    int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
+    uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
+
+    static int EffectiveSignificandSize(int order) {
+        if (order >= -1021)
+            return 53;
+        else if (order <= -1074)
+            return 0;
+        else
+            return order + 1074;
+    }
+
+private:
+    static const int kSignificandSize = 52;
+    static const int kExponentBias = 0x3FF;
+    static const int kDenormalExponent = 1 - kExponentBias;
+    static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
+    static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+    static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+    static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+    union {
+        double d_;
+        uint64_t u_;
+    };
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_IEEE754_
diff --git a/src/lottie/rapidjson/internal/itoa.h b/src/lottie/rapidjson/internal/itoa.h
new file mode 100644 (file)
index 0000000..01a4e7e
--- /dev/null
@@ -0,0 +1,304 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ITOA_
+#define RAPIDJSON_ITOA_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline const char* GetDigitsLut() {
+    static const char cDigitsLut[200] = {
+        '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
+        '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
+        '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
+        '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
+        '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
+        '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
+        '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
+        '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
+        '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
+        '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
+    };
+    return cDigitsLut;
+}
+
+inline char* u32toa(uint32_t value, char* buffer) {
+    const char* cDigitsLut = GetDigitsLut();
+
+    if (value < 10000) {
+        const uint32_t d1 = (value / 100) << 1;
+        const uint32_t d2 = (value % 100) << 1;
+        
+        if (value >= 1000)
+            *buffer++ = cDigitsLut[d1];
+        if (value >= 100)
+            *buffer++ = cDigitsLut[d1 + 1];
+        if (value >= 10)
+            *buffer++ = cDigitsLut[d2];
+        *buffer++ = cDigitsLut[d2 + 1];
+    }
+    else if (value < 100000000) {
+        // value = bbbbcccc
+        const uint32_t b = value / 10000;
+        const uint32_t c = value % 10000;
+        
+        const uint32_t d1 = (b / 100) << 1;
+        const uint32_t d2 = (b % 100) << 1;
+        
+        const uint32_t d3 = (c / 100) << 1;
+        const uint32_t d4 = (c % 100) << 1;
+        
+        if (value >= 10000000)
+            *buffer++ = cDigitsLut[d1];
+        if (value >= 1000000)
+            *buffer++ = cDigitsLut[d1 + 1];
+        if (value >= 100000)
+            *buffer++ = cDigitsLut[d2];
+        *buffer++ = cDigitsLut[d2 + 1];
+        
+        *buffer++ = cDigitsLut[d3];
+        *buffer++ = cDigitsLut[d3 + 1];
+        *buffer++ = cDigitsLut[d4];
+        *buffer++ = cDigitsLut[d4 + 1];
+    }
+    else {
+        // value = aabbbbcccc in decimal
+        
+        const uint32_t a = value / 100000000; // 1 to 42
+        value %= 100000000;
+        
+        if (a >= 10) {
+            const unsigned i = a << 1;
+            *buffer++ = cDigitsLut[i];
+            *buffer++ = cDigitsLut[i + 1];
+        }
+        else
+            *buffer++ = static_cast<char>('0' + static_cast<char>(a));
+
+        const uint32_t b = value / 10000; // 0 to 9999
+        const uint32_t c = value % 10000; // 0 to 9999
+        
+        const uint32_t d1 = (b / 100) << 1;
+        const uint32_t d2 = (b % 100) << 1;
+        
+        const uint32_t d3 = (c / 100) << 1;
+        const uint32_t d4 = (c % 100) << 1;
+        
+        *buffer++ = cDigitsLut[d1];
+        *buffer++ = cDigitsLut[d1 + 1];
+        *buffer++ = cDigitsLut[d2];
+        *buffer++ = cDigitsLut[d2 + 1];
+        *buffer++ = cDigitsLut[d3];
+        *buffer++ = cDigitsLut[d3 + 1];
+        *buffer++ = cDigitsLut[d4];
+        *buffer++ = cDigitsLut[d4 + 1];
+    }
+    return buffer;
+}
+
+inline char* i32toa(int32_t value, char* buffer) {
+    uint32_t u = static_cast<uint32_t>(value);
+    if (value < 0) {
+        *buffer++ = '-';
+        u = ~u + 1;
+    }
+
+    return u32toa(u, buffer);
+}
+
+inline char* u64toa(uint64_t value, char* buffer) {
+    const char* cDigitsLut = GetDigitsLut();
+    const uint64_t  kTen8 = 100000000;
+    const uint64_t  kTen9 = kTen8 * 10;
+    const uint64_t kTen10 = kTen8 * 100;
+    const uint64_t kTen11 = kTen8 * 1000;
+    const uint64_t kTen12 = kTen8 * 10000;
+    const uint64_t kTen13 = kTen8 * 100000;
+    const uint64_t kTen14 = kTen8 * 1000000;
+    const uint64_t kTen15 = kTen8 * 10000000;
+    const uint64_t kTen16 = kTen8 * kTen8;
+    
+    if (value < kTen8) {
+        uint32_t v = static_cast<uint32_t>(value);
+        if (v < 10000) {
+            const uint32_t d1 = (v / 100) << 1;
+            const uint32_t d2 = (v % 100) << 1;
+            
+            if (v >= 1000)
+                *buffer++ = cDigitsLut[d1];
+            if (v >= 100)
+                *buffer++ = cDigitsLut[d1 + 1];
+            if (v >= 10)
+                *buffer++ = cDigitsLut[d2];
+            *buffer++ = cDigitsLut[d2 + 1];
+        }
+        else {
+            // value = bbbbcccc
+            const uint32_t b = v / 10000;
+            const uint32_t c = v % 10000;
+            
+            const uint32_t d1 = (b / 100) << 1;
+            const uint32_t d2 = (b % 100) << 1;
+            
+            const uint32_t d3 = (c / 100) << 1;
+            const uint32_t d4 = (c % 100) << 1;
+            
+            if (value >= 10000000)
+                *buffer++ = cDigitsLut[d1];
+            if (value >= 1000000)
+                *buffer++ = cDigitsLut[d1 + 1];
+            if (value >= 100000)
+                *buffer++ = cDigitsLut[d2];
+            *buffer++ = cDigitsLut[d2 + 1];
+            
+            *buffer++ = cDigitsLut[d3];
+            *buffer++ = cDigitsLut[d3 + 1];
+            *buffer++ = cDigitsLut[d4];
+            *buffer++ = cDigitsLut[d4 + 1];
+        }
+    }
+    else if (value < kTen16) {
+        const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
+        const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
+        
+        const uint32_t b0 = v0 / 10000;
+        const uint32_t c0 = v0 % 10000;
+        
+        const uint32_t d1 = (b0 / 100) << 1;
+        const uint32_t d2 = (b0 % 100) << 1;
+        
+        const uint32_t d3 = (c0 / 100) << 1;
+        const uint32_t d4 = (c0 % 100) << 1;
+
+        const uint32_t b1 = v1 / 10000;
+        const uint32_t c1 = v1 % 10000;
+        
+        const uint32_t d5 = (b1 / 100) << 1;
+        const uint32_t d6 = (b1 % 100) << 1;
+        
+        const uint32_t d7 = (c1 / 100) << 1;
+        const uint32_t d8 = (c1 % 100) << 1;
+
+        if (value >= kTen15)
+            *buffer++ = cDigitsLut[d1];
+        if (value >= kTen14)
+            *buffer++ = cDigitsLut[d1 + 1];
+        if (value >= kTen13)
+            *buffer++ = cDigitsLut[d2];
+        if (value >= kTen12)
+            *buffer++ = cDigitsLut[d2 + 1];
+        if (value >= kTen11)
+            *buffer++ = cDigitsLut[d3];
+        if (value >= kTen10)
+            *buffer++ = cDigitsLut[d3 + 1];
+        if (value >= kTen9)
+            *buffer++ = cDigitsLut[d4];
+        if (value >= kTen8)
+            *buffer++ = cDigitsLut[d4 + 1];
+        
+        *buffer++ = cDigitsLut[d5];
+        *buffer++ = cDigitsLut[d5 + 1];
+        *buffer++ = cDigitsLut[d6];
+        *buffer++ = cDigitsLut[d6 + 1];
+        *buffer++ = cDigitsLut[d7];
+        *buffer++ = cDigitsLut[d7 + 1];
+        *buffer++ = cDigitsLut[d8];
+        *buffer++ = cDigitsLut[d8 + 1];
+    }
+    else {
+        const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
+        value %= kTen16;
+        
+        if (a < 10)
+            *buffer++ = static_cast<char>('0' + static_cast<char>(a));
+        else if (a < 100) {
+            const uint32_t i = a << 1;
+            *buffer++ = cDigitsLut[i];
+            *buffer++ = cDigitsLut[i + 1];
+        }
+        else if (a < 1000) {
+            *buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
+            
+            const uint32_t i = (a % 100) << 1;
+            *buffer++ = cDigitsLut[i];
+            *buffer++ = cDigitsLut[i + 1];
+        }
+        else {
+            const uint32_t i = (a / 100) << 1;
+            const uint32_t j = (a % 100) << 1;
+            *buffer++ = cDigitsLut[i];
+            *buffer++ = cDigitsLut[i + 1];
+            *buffer++ = cDigitsLut[j];
+            *buffer++ = cDigitsLut[j + 1];
+        }
+        
+        const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
+        const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
+        
+        const uint32_t b0 = v0 / 10000;
+        const uint32_t c0 = v0 % 10000;
+        
+        const uint32_t d1 = (b0 / 100) << 1;
+        const uint32_t d2 = (b0 % 100) << 1;
+        
+        const uint32_t d3 = (c0 / 100) << 1;
+        const uint32_t d4 = (c0 % 100) << 1;
+        
+        const uint32_t b1 = v1 / 10000;
+        const uint32_t c1 = v1 % 10000;
+        
+        const uint32_t d5 = (b1 / 100) << 1;
+        const uint32_t d6 = (b1 % 100) << 1;
+        
+        const uint32_t d7 = (c1 / 100) << 1;
+        const uint32_t d8 = (c1 % 100) << 1;
+        
+        *buffer++ = cDigitsLut[d1];
+        *buffer++ = cDigitsLut[d1 + 1];
+        *buffer++ = cDigitsLut[d2];
+        *buffer++ = cDigitsLut[d2 + 1];
+        *buffer++ = cDigitsLut[d3];
+        *buffer++ = cDigitsLut[d3 + 1];
+        *buffer++ = cDigitsLut[d4];
+        *buffer++ = cDigitsLut[d4 + 1];
+        *buffer++ = cDigitsLut[d5];
+        *buffer++ = cDigitsLut[d5 + 1];
+        *buffer++ = cDigitsLut[d6];
+        *buffer++ = cDigitsLut[d6 + 1];
+        *buffer++ = cDigitsLut[d7];
+        *buffer++ = cDigitsLut[d7 + 1];
+        *buffer++ = cDigitsLut[d8];
+        *buffer++ = cDigitsLut[d8 + 1];
+    }
+    
+    return buffer;
+}
+
+inline char* i64toa(int64_t value, char* buffer) {
+    uint64_t u = static_cast<uint64_t>(value);
+    if (value < 0) {
+        *buffer++ = '-';
+        u = ~u + 1;
+    }
+
+    return u64toa(u, buffer);
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ITOA_
diff --git a/src/lottie/rapidjson/internal/meta.h b/src/lottie/rapidjson/internal/meta.h
new file mode 100644 (file)
index 0000000..5a9aaa4
--- /dev/null
@@ -0,0 +1,181 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_META_H_
+#define RAPIDJSON_INTERNAL_META_H_
+
+#include "../rapidjson.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+#if defined(_MSC_VER)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(6334)
+#endif
+
+#if RAPIDJSON_HAS_CXX11_TYPETRAITS
+#include <type_traits>
+#endif
+
+//@cond RAPIDJSON_INTERNAL
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
+template <typename T> struct Void { typedef void Type; };
+
+///////////////////////////////////////////////////////////////////////////////
+// BoolType, TrueType, FalseType
+//
+template <bool Cond> struct BoolType {
+    static const bool Value = Cond;
+    typedef BoolType Type;
+};
+typedef BoolType<true> TrueType;
+typedef BoolType<false> FalseType;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
+//
+
+template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
+template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
+template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
+template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
+
+template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
+template <> struct AndExprCond<true, true> : TrueType {};
+template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
+template <> struct OrExprCond<false, false> : FalseType {};
+
+template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
+template <typename C> struct NotExpr  : SelectIf<C,FalseType,TrueType>::Type {};
+template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
+template <typename C1, typename C2> struct OrExpr  : OrExprCond<C1::Value, C2::Value>::Type {};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// AddConst, MaybeAddConst, RemoveConst
+template <typename T> struct AddConst { typedef const T Type; };
+template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
+template <typename T> struct RemoveConst { typedef T Type; };
+template <typename T> struct RemoveConst<const T> { typedef T Type; };
+
+
+///////////////////////////////////////////////////////////////////////////////
+// IsSame, IsConst, IsMoreConst, IsPointer
+//
+template <typename T, typename U> struct IsSame : FalseType {};
+template <typename T> struct IsSame<T, T> : TrueType {};
+
+template <typename T> struct IsConst : FalseType {};
+template <typename T> struct IsConst<const T> : TrueType {};
+
+template <typename CT, typename T>
+struct IsMoreConst
+    : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
+              BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
+
+template <typename T> struct IsPointer : FalseType {};
+template <typename T> struct IsPointer<T*> : TrueType {};
+
+///////////////////////////////////////////////////////////////////////////////
+// IsBaseOf
+//
+#if RAPIDJSON_HAS_CXX11_TYPETRAITS
+
+template <typename B, typename D> struct IsBaseOf
+    : BoolType< ::std::is_base_of<B,D>::value> {};
+
+#else // simplified version adopted from Boost
+
+template<typename B, typename D> struct IsBaseOfImpl {
+    RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
+    RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
+
+    typedef char (&Yes)[1];
+    typedef char (&No) [2];
+
+    template <typename T>
+    static Yes Check(const D*, T);
+    static No  Check(const B*, int);
+
+    struct Host {
+        operator const B*() const;
+        operator const D*();
+    };
+
+    enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
+};
+
+template <typename B, typename D> struct IsBaseOf
+    : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
+
+#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
+
+
+//////////////////////////////////////////////////////////////////////////
+// EnableIf / DisableIf
+//
+template <bool Condition, typename T = void> struct EnableIfCond  { typedef T Type; };
+template <typename T> struct EnableIfCond<false, T> { /* empty */ };
+
+template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
+template <typename T> struct DisableIfCond<true, T> { /* empty */ };
+
+template <typename Condition, typename T = void>
+struct EnableIf : EnableIfCond<Condition::Value, T> {};
+
+template <typename Condition, typename T = void>
+struct DisableIf : DisableIfCond<Condition::Value, T> {};
+
+// SFINAE helpers
+struct SfinaeTag {};
+template <typename T> struct RemoveSfinaeTag;
+template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
+
+#define RAPIDJSON_REMOVEFPTR_(type) \
+    typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
+        < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
+
+#define RAPIDJSON_ENABLEIF(cond) \
+    typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
+        <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
+
+#define RAPIDJSON_DISABLEIF(cond) \
+    typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
+        <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
+
+#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
+    typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
+        <RAPIDJSON_REMOVEFPTR_(cond), \
+         RAPIDJSON_REMOVEFPTR_(returntype)>::Type
+
+#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
+    typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
+        <RAPIDJSON_REMOVEFPTR_(cond), \
+         RAPIDJSON_REMOVEFPTR_(returntype)>::Type
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+//@endcond
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_META_H_
diff --git a/src/lottie/rapidjson/internal/pow10.h b/src/lottie/rapidjson/internal/pow10.h
new file mode 100644 (file)
index 0000000..02f475d
--- /dev/null
@@ -0,0 +1,55 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_POW10_
+#define RAPIDJSON_POW10_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Computes integer powers of 10 in double (10.0^n).
+/*! This function uses lookup table for fast and accurate results.
+    \param n non-negative exponent. Must <= 308.
+    \return 10.0^n
+*/
+inline double Pow10(int n) {
+    static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
+        1e+0,  
+        1e+1,  1e+2,  1e+3,  1e+4,  1e+5,  1e+6,  1e+7,  1e+8,  1e+9,  1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 
+        1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
+        1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
+        1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
+        1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
+        1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
+        1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
+        1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
+        1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
+        1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
+        1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
+        1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
+        1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
+        1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
+        1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
+        1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
+    };
+    RAPIDJSON_ASSERT(n >= 0 && n <= 308);
+    return e[n];
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_POW10_
diff --git a/src/lottie/rapidjson/internal/regex.h b/src/lottie/rapidjson/internal/regex.h
new file mode 100644 (file)
index 0000000..e1a2faa
--- /dev/null
@@ -0,0 +1,734 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_REGEX_H_
+#define RAPIDJSON_INTERNAL_REGEX_H_
+
+#include "../allocators.h"
+#include "../stream.h"
+#include "stack.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(implicit-fallthrough)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#if __GNUC__ >= 7
+RAPIDJSON_DIAG_OFF(implicit-fallthrough)
+#endif
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+#ifndef RAPIDJSON_REGEX_VERBOSE
+#define RAPIDJSON_REGEX_VERBOSE 0
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// DecodedStream
+
+template <typename SourceStream, typename Encoding>
+class DecodedStream {
+public:
+    DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
+    unsigned Peek() { return codepoint_; }
+    unsigned Take() {
+        unsigned c = codepoint_;
+        if (c) // No further decoding when '\0'
+            Decode();
+        return c;
+    }
+
+private:
+    void Decode() {
+        if (!Encoding::Decode(ss_, &codepoint_))
+            codepoint_ = 0;
+    }
+
+    SourceStream& ss_;
+    unsigned codepoint_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericRegex
+
+static const SizeType kRegexInvalidState = ~SizeType(0);  //!< Represents an invalid index in GenericRegex::State::out, out1
+static const SizeType kRegexInvalidRange = ~SizeType(0);
+
+template <typename Encoding, typename Allocator>
+class GenericRegexSearch;
+
+//! Regular expression engine with subset of ECMAscript grammar.
+/*!
+    Supported regular expression syntax:
+    - \c ab     Concatenation
+    - \c a|b    Alternation
+    - \c a?     Zero or one
+    - \c a*     Zero or more
+    - \c a+     One or more
+    - \c a{3}   Exactly 3 times
+    - \c a{3,}  At least 3 times
+    - \c a{3,5} 3 to 5 times
+    - \c (ab)   Grouping
+    - \c ^a     At the beginning
+    - \c a$     At the end
+    - \c .      Any character
+    - \c [abc]  Character classes
+    - \c [a-c]  Character class range
+    - \c [a-z0-9_] Character class combination
+    - \c [^abc] Negated character classes
+    - \c [^a-c] Negated character class range
+    - \c [\b]   Backspace (U+0008)
+    - \c \\| \\\\ ...  Escape characters
+    - \c \\f Form feed (U+000C)
+    - \c \\n Line feed (U+000A)
+    - \c \\r Carriage return (U+000D)
+    - \c \\t Tab (U+0009)
+    - \c \\v Vertical tab (U+000B)
+
+    \note This is a Thompson NFA engine, implemented with reference to 
+        Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", 
+        https://swtch.com/~rsc/regexp/regexp1.html 
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericRegex {
+public:
+    typedef Encoding EncodingType;
+    typedef typename Encoding::Ch Ch;
+    template <typename, typename> friend class GenericRegexSearch;
+
+    GenericRegex(const Ch* source, Allocator* allocator = 0) : 
+        states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), 
+        anchorBegin_(), anchorEnd_()
+    {
+        GenericStringStream<Encoding> ss(source);
+        DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss);
+        Parse(ds);
+    }
+
+    ~GenericRegex() {}
+
+    bool IsValid() const {
+        return root_ != kRegexInvalidState;
+    }
+
+private:
+    enum Operator {
+        kZeroOrOne,
+        kZeroOrMore,
+        kOneOrMore,
+        kConcatenation,
+        kAlternation,
+        kLeftParenthesis
+    };
+
+    static const unsigned kAnyCharacterClass = 0xFFFFFFFF;   //!< For '.'
+    static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
+    static const unsigned kRangeNegationFlag = 0x80000000;
+
+    struct Range {
+        unsigned start; // 
+        unsigned end;
+        SizeType next;
+    };
+
+    struct State {
+        SizeType out;     //!< Equals to kInvalid for matching state
+        SizeType out1;    //!< Equals to non-kInvalid for split
+        SizeType rangeStart;
+        unsigned codepoint;
+    };
+
+    struct Frag {
+        Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
+        SizeType start;
+        SizeType out; //!< link-list of all output states
+        SizeType minIndex;
+    };
+
+    State& GetState(SizeType index) {
+        RAPIDJSON_ASSERT(index < stateCount_);
+        return states_.template Bottom<State>()[index];
+    }
+
+    const State& GetState(SizeType index) const {
+        RAPIDJSON_ASSERT(index < stateCount_);
+        return states_.template Bottom<State>()[index];
+    }
+
+    Range& GetRange(SizeType index) {
+        RAPIDJSON_ASSERT(index < rangeCount_);
+        return ranges_.template Bottom<Range>()[index];
+    }
+
+    const Range& GetRange(SizeType index) const {
+        RAPIDJSON_ASSERT(index < rangeCount_);
+        return ranges_.template Bottom<Range>()[index];
+    }
+
+    template <typename InputStream>
+    void Parse(DecodedStream<InputStream, Encoding>& ds) {
+        Allocator allocator;
+        Stack<Allocator> operandStack(&allocator, 256);     // Frag
+        Stack<Allocator> operatorStack(&allocator, 256);    // Operator
+        Stack<Allocator> atomCountStack(&allocator, 256);   // unsigned (Atom per parenthesis)
+
+        *atomCountStack.template Push<unsigned>() = 0;
+
+        unsigned codepoint;
+        while (ds.Peek() != 0) {
+            switch (codepoint = ds.Take()) {
+                case '^':
+                    anchorBegin_ = true;
+                    break;
+
+                case '$':
+                    anchorEnd_ = true;
+                    break;
+
+                case '|':
+                    while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
+                        if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+                            return;
+                    *operatorStack.template Push<Operator>() = kAlternation;
+                    *atomCountStack.template Top<unsigned>() = 0;
+                    break;
+
+                case '(':
+                    *operatorStack.template Push<Operator>() = kLeftParenthesis;
+                    *atomCountStack.template Push<unsigned>() = 0;
+                    break;
+
+                case ')':
+                    while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
+                        if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+                            return;
+                    if (operatorStack.Empty())
+                        return;
+                    operatorStack.template Pop<Operator>(1);
+                    atomCountStack.template Pop<unsigned>(1);
+                    ImplicitConcatenation(atomCountStack, operatorStack);
+                    break;
+
+                case '?':
+                    if (!Eval(operandStack, kZeroOrOne))
+                        return;
+                    break;
+
+                case '*':
+                    if (!Eval(operandStack, kZeroOrMore))
+                        return;
+                    break;
+
+                case '+':
+                    if (!Eval(operandStack, kOneOrMore))
+                        return;
+                    break;
+
+                case '{':
+                    {
+                        unsigned n, m;
+                        if (!ParseUnsigned(ds, &n))
+                            return;
+
+                        if (ds.Peek() == ',') {
+                            ds.Take();
+                            if (ds.Peek() == '}')
+                                m = kInfinityQuantifier;
+                            else if (!ParseUnsigned(ds, &m) || m < n)
+                                return;
+                        }
+                        else
+                            m = n;
+
+                        if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
+                            return;
+                        ds.Take();
+                    }
+                    break;
+
+                case '.':
+                    PushOperand(operandStack, kAnyCharacterClass);
+                    ImplicitConcatenation(atomCountStack, operatorStack);
+                    break;
+
+                case '[':
+                    {
+                        SizeType range;
+                        if (!ParseRange(ds, &range))
+                            return;
+                        SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
+                        GetState(s).rangeStart = range;
+                        *operandStack.template Push<Frag>() = Frag(s, s, s);
+                    }
+                    ImplicitConcatenation(atomCountStack, operatorStack);
+                    break;
+
+                case '\\': // Escape character
+                    if (!CharacterEscape(ds, &codepoint))
+                        return; // Unsupported escape character
+                    // fall through to default
+
+                default: // Pattern character
+                    PushOperand(operandStack, codepoint);
+                    ImplicitConcatenation(atomCountStack, operatorStack);
+            }
+        }
+
+        while (!operatorStack.Empty())
+            if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+                return;
+
+        // Link the operand to matching state.
+        if (operandStack.GetSize() == sizeof(Frag)) {
+            Frag* e = operandStack.template Pop<Frag>(1);
+            Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
+            root_ = e->start;
+
+#if RAPIDJSON_REGEX_VERBOSE
+            printf("root: %d\n", root_);
+            for (SizeType i = 0; i < stateCount_ ; i++) {
+                State& s = GetState(i);
+                printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
+            }
+            printf("\n");
+#endif
+        }
+    }
+
+    SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
+        State* s = states_.template Push<State>();
+        s->out = out;
+        s->out1 = out1;
+        s->codepoint = codepoint;
+        s->rangeStart = kRegexInvalidRange;
+        return stateCount_++;
+    }
+
+    void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
+        SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
+        *operandStack.template Push<Frag>() = Frag(s, s, s);
+    }
+
+    void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
+        if (*atomCountStack.template Top<unsigned>())
+            *operatorStack.template Push<Operator>() = kConcatenation;
+        (*atomCountStack.template Top<unsigned>())++;
+    }
+
+    SizeType Append(SizeType l1, SizeType l2) {
+        SizeType old = l1;
+        while (GetState(l1).out != kRegexInvalidState)
+            l1 = GetState(l1).out;
+        GetState(l1).out = l2;
+        return old;
+    }
+
+    void Patch(SizeType l, SizeType s) {
+        for (SizeType next; l != kRegexInvalidState; l = next) {
+            next = GetState(l).out;
+            GetState(l).out = s;
+        }
+    }
+
+    bool Eval(Stack<Allocator>& operandStack, Operator op) {
+        switch (op) {
+            case kConcatenation:
+                RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
+                {
+                    Frag e2 = *operandStack.template Pop<Frag>(1);
+                    Frag e1 = *operandStack.template Pop<Frag>(1);
+                    Patch(e1.out, e2.start);
+                    *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
+                }
+                return true;
+
+            case kAlternation:
+                if (operandStack.GetSize() >= sizeof(Frag) * 2) {
+                    Frag e2 = *operandStack.template Pop<Frag>(1);
+                    Frag e1 = *operandStack.template Pop<Frag>(1);
+                    SizeType s = NewState(e1.start, e2.start, 0);
+                    *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
+                    return true;
+                }
+                return false;
+
+            case kZeroOrOne:
+                if (operandStack.GetSize() >= sizeof(Frag)) {
+                    Frag e = *operandStack.template Pop<Frag>(1);
+                    SizeType s = NewState(kRegexInvalidState, e.start, 0);
+                    *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
+                    return true;
+                }
+                return false;
+
+            case kZeroOrMore:
+                if (operandStack.GetSize() >= sizeof(Frag)) {
+                    Frag e = *operandStack.template Pop<Frag>(1);
+                    SizeType s = NewState(kRegexInvalidState, e.start, 0);
+                    Patch(e.out, s);
+                    *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
+                    return true;
+                }
+                return false;
+
+            default: 
+                RAPIDJSON_ASSERT(op == kOneOrMore);
+                if (operandStack.GetSize() >= sizeof(Frag)) {
+                    Frag e = *operandStack.template Pop<Frag>(1);
+                    SizeType s = NewState(kRegexInvalidState, e.start, 0);
+                    Patch(e.out, s);
+                    *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
+                    return true;
+                }
+                return false;
+        }
+    }
+
+    bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
+        RAPIDJSON_ASSERT(n <= m);
+        RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
+
+        if (n == 0) {
+            if (m == 0)                             // a{0} not support
+                return false;
+            else if (m == kInfinityQuantifier)
+                Eval(operandStack, kZeroOrMore);    // a{0,} -> a*
+            else {
+                Eval(operandStack, kZeroOrOne);         // a{0,5} -> a?
+                for (unsigned i = 0; i < m - 1; i++)
+                    CloneTopOperand(operandStack);      // a{0,5} -> a? a? a? a? a?
+                for (unsigned i = 0; i < m - 1; i++)
+                    Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
+            }
+            return true;
+        }
+
+        for (unsigned i = 0; i < n - 1; i++)        // a{3} -> a a a
+            CloneTopOperand(operandStack);
+
+        if (m == kInfinityQuantifier)
+            Eval(operandStack, kOneOrMore);         // a{3,} -> a a a+
+        else if (m > n) {
+            CloneTopOperand(operandStack);          // a{3,5} -> a a a a
+            Eval(operandStack, kZeroOrOne);         // a{3,5} -> a a a a?
+            for (unsigned i = n; i < m - 1; i++)
+                CloneTopOperand(operandStack);      // a{3,5} -> a a a a? a?
+            for (unsigned i = n; i < m; i++)
+                Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
+        }
+
+        for (unsigned i = 0; i < n - 1; i++)
+            Eval(operandStack, kConcatenation);     // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
+
+        return true;
+    }
+
+    static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
+
+    void CloneTopOperand(Stack<Allocator>& operandStack) {
+        const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
+        SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
+        State* s = states_.template Push<State>(count);
+        memcpy(s, &GetState(src.minIndex), count * sizeof(State));
+        for (SizeType j = 0; j < count; j++) {
+            if (s[j].out != kRegexInvalidState)
+                s[j].out += count;
+            if (s[j].out1 != kRegexInvalidState)
+                s[j].out1 += count;
+        }
+        *operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
+        stateCount_ += count;
+    }
+
+    template <typename InputStream>
+    bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) {
+        unsigned r = 0;
+        if (ds.Peek() < '0' || ds.Peek() > '9')
+            return false;
+        while (ds.Peek() >= '0' && ds.Peek() <= '9') {
+            if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
+                return false; // overflow
+            r = r * 10 + (ds.Take() - '0');
+        }
+        *u = r;
+        return true;
+    }
+
+    template <typename InputStream>
+    bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) {
+        bool isBegin = true;
+        bool negate = false;
+        int step = 0;
+        SizeType start = kRegexInvalidRange;
+        SizeType current = kRegexInvalidRange;
+        unsigned codepoint;
+        while ((codepoint = ds.Take()) != 0) {
+            if (isBegin) {
+                isBegin = false;
+                if (codepoint == '^') {
+                    negate = true;
+                    continue;
+                }
+            }
+
+            switch (codepoint) {
+            case ']':
+                if (start == kRegexInvalidRange)
+                    return false;   // Error: nothing inside []
+                if (step == 2) { // Add trailing '-'
+                    SizeType r = NewRange('-');
+                    RAPIDJSON_ASSERT(current != kRegexInvalidRange);
+                    GetRange(current).next = r;
+                }
+                if (negate)
+                    GetRange(start).start |= kRangeNegationFlag;
+                *range = start;
+                return true;
+
+            case '\\':
+                if (ds.Peek() == 'b') {
+                    ds.Take();
+                    codepoint = 0x0008; // Escape backspace character
+                }
+                else if (!CharacterEscape(ds, &codepoint))
+                    return false;
+                // fall through to default
+
+            default:
+                switch (step) {
+                case 1:
+                    if (codepoint == '-') {
+                        step++;
+                        break;
+                    }
+                    // fall through to step 0 for other characters
+
+                case 0:
+                    {
+                        SizeType r = NewRange(codepoint);
+                        if (current != kRegexInvalidRange)
+                            GetRange(current).next = r;
+                        if (start == kRegexInvalidRange)
+                            start = r;
+                        current = r;
+                    }
+                    step = 1;
+                    break;
+
+                default:
+                    RAPIDJSON_ASSERT(step == 2);
+                    GetRange(current).end = codepoint;
+                    step = 0;
+                }
+            }
+        }
+        return false;
+    }
+    
+    SizeType NewRange(unsigned codepoint) {
+        Range* r = ranges_.template Push<Range>();
+        r->start = r->end = codepoint;
+        r->next = kRegexInvalidRange;
+        return rangeCount_++;
+    }
+
+    template <typename InputStream>
+    bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) {
+        unsigned codepoint;
+        switch (codepoint = ds.Take()) {
+            case '^':
+            case '$':
+            case '|':
+            case '(':
+            case ')':
+            case '?':
+            case '*':
+            case '+':
+            case '.':
+            case '[':
+            case ']':
+            case '{':
+            case '}':
+            case '\\':
+                *escapedCodepoint = codepoint; return true;
+            case 'f': *escapedCodepoint = 0x000C; return true;
+            case 'n': *escapedCodepoint = 0x000A; return true;
+            case 'r': *escapedCodepoint = 0x000D; return true;
+            case 't': *escapedCodepoint = 0x0009; return true;
+            case 'v': *escapedCodepoint = 0x000B; return true;
+            default:
+                return false; // Unsupported escape character
+        }
+    }
+
+    Stack<Allocator> states_;
+    Stack<Allocator> ranges_;
+    SizeType root_;
+    SizeType stateCount_;
+    SizeType rangeCount_;
+
+    static const unsigned kInfinityQuantifier = ~0u;
+
+    // For SearchWithAnchoring()
+    bool anchorBegin_;
+    bool anchorEnd_;
+};
+
+template <typename RegexType, typename Allocator = CrtAllocator>
+class GenericRegexSearch {
+public:
+    typedef typename RegexType::EncodingType Encoding;
+    typedef typename Encoding::Ch Ch;
+
+    GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : 
+        regex_(regex), allocator_(allocator), ownAllocator_(0),
+        state0_(allocator, 0), state1_(allocator, 0), stateSet_()
+    {
+        RAPIDJSON_ASSERT(regex_.IsValid());
+        if (!allocator_)
+            ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+        stateSet_ = static_cast<unsigned*>(allocator_->Malloc(GetStateSetSize()));
+        state0_.template Reserve<SizeType>(regex_.stateCount_);
+        state1_.template Reserve<SizeType>(regex_.stateCount_);
+    }
+
+    ~GenericRegexSearch() {
+        Allocator::Free(stateSet_);
+        RAPIDJSON_DELETE(ownAllocator_);
+    }
+
+    template <typename InputStream>
+    bool Match(InputStream& is) {
+        return SearchWithAnchoring(is, true, true);
+    }
+
+    bool Match(const Ch* s) {
+        GenericStringStream<Encoding> is(s);
+        return Match(is);
+    }
+
+    template <typename InputStream>
+    bool Search(InputStream& is) {
+        return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_);
+    }
+
+    bool Search(const Ch* s) {
+        GenericStringStream<Encoding> is(s);
+        return Search(is);
+    }
+
+private:
+    typedef typename RegexType::State State;
+    typedef typename RegexType::Range Range;
+
+    template <typename InputStream>
+    bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) {
+        DecodedStream<InputStream, Encoding> ds(is);
+
+        state0_.Clear();
+        Stack<Allocator> *current = &state0_, *next = &state1_;
+        const size_t stateSetSize = GetStateSetSize();
+        std::memset(stateSet_, 0, stateSetSize);
+
+        bool matched = AddState(*current, regex_.root_);
+        unsigned codepoint;
+        while (!current->Empty() && (codepoint = ds.Take()) != 0) {
+            std::memset(stateSet_, 0, stateSetSize);
+            next->Clear();
+            matched = false;
+            for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
+                const State& sr = regex_.GetState(*s);
+                if (sr.codepoint == codepoint ||
+                    sr.codepoint == RegexType::kAnyCharacterClass || 
+                    (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
+                {
+                    matched = AddState(*next, sr.out) || matched;
+                    if (!anchorEnd && matched)
+                        return true;
+                }
+                if (!anchorBegin)
+                    AddState(*next, regex_.root_);
+            }
+            internal::Swap(current, next);
+        }
+
+        return matched;
+    }
+
+    size_t GetStateSetSize() const {
+        return (regex_.stateCount_ + 31) / 32 * 4;
+    }
+
+    // Return whether the added states is a match state
+    bool AddState(Stack<Allocator>& l, SizeType index) {
+        RAPIDJSON_ASSERT(index != kRegexInvalidState);
+
+        const State& s = regex_.GetState(index);
+        if (s.out1 != kRegexInvalidState) { // Split
+            bool matched = AddState(l, s.out);
+            return AddState(l, s.out1) || matched;
+        }
+        else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) {
+            stateSet_[index >> 5] |= (1u << (index & 31));
+            *l.template PushUnsafe<SizeType>() = index;
+        }
+        return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
+    }
+
+    bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
+        bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0;
+        while (rangeIndex != kRegexInvalidRange) {
+            const Range& r = regex_.GetRange(rangeIndex);
+            if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end)
+                return yes;
+            rangeIndex = r.next;
+        }
+        return !yes;
+    }
+
+    const RegexType& regex_;
+    Allocator* allocator_;
+    Allocator* ownAllocator_;
+    Stack<Allocator> state0_;
+    Stack<Allocator> state1_;
+    uint32_t* stateSet_;
+};
+
+typedef GenericRegex<UTF8<> > Regex;
+typedef GenericRegexSearch<Regex> RegexSearch;
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_REGEX_H_
diff --git a/src/lottie/rapidjson/internal/stack.h b/src/lottie/rapidjson/internal/stack.h
new file mode 100644 (file)
index 0000000..5c5398c
--- /dev/null
@@ -0,0 +1,231 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_STACK_H_
+#define RAPIDJSON_INTERNAL_STACK_H_
+
+#include "../allocators.h"
+#include "swap.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// Stack
+
+//! A type-unsafe stack for storing different types of data.
+/*! \tparam Allocator Allocator for allocating stack memory.
+*/
+template <typename Allocator>
+class Stack {
+public:
+    // Optimization note: Do not allocate memory for stack_ in constructor.
+    // Do it lazily when first Push() -> Expand() -> Resize().
+    Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    Stack(Stack&& rhs)
+        : allocator_(rhs.allocator_),
+          ownAllocator_(rhs.ownAllocator_),
+          stack_(rhs.stack_),
+          stackTop_(rhs.stackTop_),
+          stackEnd_(rhs.stackEnd_),
+          initialCapacity_(rhs.initialCapacity_)
+    {
+        rhs.allocator_ = 0;
+        rhs.ownAllocator_ = 0;
+        rhs.stack_ = 0;
+        rhs.stackTop_ = 0;
+        rhs.stackEnd_ = 0;
+        rhs.initialCapacity_ = 0;
+    }
+#endif
+
+    ~Stack() {
+        Destroy();
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    Stack& operator=(Stack&& rhs) {
+        if (&rhs != this)
+        {
+            Destroy();
+
+            allocator_ = rhs.allocator_;
+            ownAllocator_ = rhs.ownAllocator_;
+            stack_ = rhs.stack_;
+            stackTop_ = rhs.stackTop_;
+            stackEnd_ = rhs.stackEnd_;
+            initialCapacity_ = rhs.initialCapacity_;
+
+            rhs.allocator_ = 0;
+            rhs.ownAllocator_ = 0;
+            rhs.stack_ = 0;
+            rhs.stackTop_ = 0;
+            rhs.stackEnd_ = 0;
+            rhs.initialCapacity_ = 0;
+        }
+        return *this;
+    }
+#endif
+
+    void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
+        internal::Swap(allocator_, rhs.allocator_);
+        internal::Swap(ownAllocator_, rhs.ownAllocator_);
+        internal::Swap(stack_, rhs.stack_);
+        internal::Swap(stackTop_, rhs.stackTop_);
+        internal::Swap(stackEnd_, rhs.stackEnd_);
+        internal::Swap(initialCapacity_, rhs.initialCapacity_);
+    }
+
+    void Clear() { stackTop_ = stack_; }
+
+    void ShrinkToFit() { 
+        if (Empty()) {
+            // If the stack is empty, completely deallocate the memory.
+            Allocator::Free(stack_);
+            stack_ = 0;
+            stackTop_ = 0;
+            stackEnd_ = 0;
+        }
+        else
+            Resize(GetSize());
+    }
+
+    // Optimization note: try to minimize the size of this function for force inline.
+    // Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
+    template<typename T>
+    RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
+         // Expand the stack if needed
+        if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
+            Expand<T>(count);
+    }
+
+    template<typename T>
+    RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
+        Reserve<T>(count);
+        return PushUnsafe<T>(count);
+    }
+
+    template<typename T>
+    RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
+        RAPIDJSON_ASSERT(stackTop_);
+        RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
+        T* ret = reinterpret_cast<T*>(stackTop_);
+        stackTop_ += sizeof(T) * count;
+        return ret;
+    }
+
+    template<typename T>
+    T* Pop(size_t count) {
+        RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
+        stackTop_ -= count * sizeof(T);
+        return reinterpret_cast<T*>(stackTop_);
+    }
+
+    template<typename T>
+    T* Top() { 
+        RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+        return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+    }
+
+    template<typename T>
+    const T* Top() const {
+        RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+        return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+    }
+
+    template<typename T>
+    T* End() { return reinterpret_cast<T*>(stackTop_); }
+
+    template<typename T>
+    const T* End() const { return reinterpret_cast<T*>(stackTop_); }
+
+    template<typename T>
+    T* Bottom() { return reinterpret_cast<T*>(stack_); }
+
+    template<typename T>
+    const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
+
+    bool HasAllocator() const {
+        return allocator_ != 0;
+    }
+
+    Allocator& GetAllocator() {
+        RAPIDJSON_ASSERT(allocator_);
+        return *allocator_;
+    }
+
+    bool Empty() const { return stackTop_ == stack_; }
+    size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
+    size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
+
+private:
+    template<typename T>
+    void Expand(size_t count) {
+        // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
+        size_t newCapacity;
+        if (stack_ == 0) {
+            if (!allocator_)
+                ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+            newCapacity = initialCapacity_;
+        } else {
+            newCapacity = GetCapacity();
+            newCapacity += (newCapacity + 1) / 2;
+        }
+        size_t newSize = GetSize() + sizeof(T) * count;
+        if (newCapacity < newSize)
+            newCapacity = newSize;
+
+        Resize(newCapacity);
+    }
+
+    void Resize(size_t newCapacity) {
+        const size_t size = GetSize();  // Backup the current size
+        stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
+        stackTop_ = stack_ + size;
+        stackEnd_ = stack_ + newCapacity;
+    }
+
+    void Destroy() {
+        Allocator::Free(stack_);
+        RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
+    }
+
+    // Prohibit copy constructor & assignment operator.
+    Stack(const Stack&);
+    Stack& operator=(const Stack&);
+
+    Allocator* allocator_;
+    Allocator* ownAllocator_;
+    char *stack_;
+    char *stackTop_;
+    char *stackEnd_;
+    size_t initialCapacity_;
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_STACK_H_
diff --git a/src/lottie/rapidjson/internal/strfunc.h b/src/lottie/rapidjson/internal/strfunc.h
new file mode 100644 (file)
index 0000000..226439a
--- /dev/null
@@ -0,0 +1,69 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
+#define RAPIDJSON_INTERNAL_STRFUNC_H_
+
+#include "../stream.h"
+#include <cwchar>
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Custom strlen() which works on different character types.
+/*! \tparam Ch Character type (e.g. char, wchar_t, short)
+    \param s Null-terminated input string.
+    \return Number of characters in the string. 
+    \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
+*/
+template <typename Ch>
+inline SizeType StrLen(const Ch* s) {
+    RAPIDJSON_ASSERT(s != 0);
+    const Ch* p = s;
+    while (*p) ++p;
+    return SizeType(p - s);
+}
+
+template <>
+inline SizeType StrLen(const char* s) {
+    return SizeType(std::strlen(s));
+}
+
+template <>
+inline SizeType StrLen(const wchar_t* s) {
+    return SizeType(std::wcslen(s));
+}
+
+//! Returns number of code points in a encoded string.
+template<typename Encoding>
+bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
+    RAPIDJSON_ASSERT(s != 0);
+    RAPIDJSON_ASSERT(outCount != 0);
+    GenericStringStream<Encoding> is(s);
+    const typename Encoding::Ch* end = s + length;
+    SizeType count = 0;
+    while (is.src_ < end) {
+        unsigned codepoint;
+        if (!Encoding::Decode(is, &codepoint))
+            return false;
+        count++;
+    }
+    *outCount = count;
+    return true;
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
diff --git a/src/lottie/rapidjson/internal/strtod.h b/src/lottie/rapidjson/internal/strtod.h
new file mode 100644 (file)
index 0000000..adf49e3
--- /dev/null
@@ -0,0 +1,269 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_STRTOD_
+#define RAPIDJSON_STRTOD_
+
+#include "ieee754.h"
+#include "biginteger.h"
+#include "diyfp.h"
+#include "pow10.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline double FastPath(double significand, int exp) {
+    if (exp < -308)
+        return 0.0;
+    else if (exp >= 0)
+        return significand * internal::Pow10(exp);
+    else
+        return significand / internal::Pow10(-exp);
+}
+
+inline double StrtodNormalPrecision(double d, int p) {
+    if (p < -308) {
+        // Prevent expSum < -308, making Pow10(p) = 0
+        d = FastPath(d, -308);
+        d = FastPath(d, p + 308);
+    }
+    else
+        d = FastPath(d, p);
+    return d;
+}
+
+template <typename T>
+inline T Min3(T a, T b, T c) {
+    T m = a;
+    if (m > b) m = b;
+    if (m > c) m = c;
+    return m;
+}
+
+inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
+    const Double db(b);
+    const uint64_t bInt = db.IntegerSignificand();
+    const int bExp = db.IntegerExponent();
+    const int hExp = bExp - 1;
+
+    int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
+
+    // Adjust for decimal exponent
+    if (dExp >= 0) {
+        dS_Exp2 += dExp;
+        dS_Exp5 += dExp;
+    }
+    else {
+        bS_Exp2 -= dExp;
+        bS_Exp5 -= dExp;
+        hS_Exp2 -= dExp;
+        hS_Exp5 -= dExp;
+    }
+
+    // Adjust for binary exponent
+    if (bExp >= 0)
+        bS_Exp2 += bExp;
+    else {
+        dS_Exp2 -= bExp;
+        hS_Exp2 -= bExp;
+    }
+
+    // Adjust for half ulp exponent
+    if (hExp >= 0)
+        hS_Exp2 += hExp;
+    else {
+        dS_Exp2 -= hExp;
+        bS_Exp2 -= hExp;
+    }
+
+    // Remove common power of two factor from all three scaled values
+    int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
+    dS_Exp2 -= common_Exp2;
+    bS_Exp2 -= common_Exp2;
+    hS_Exp2 -= common_Exp2;
+
+    BigInteger dS = d;
+    dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
+
+    BigInteger bS(bInt);
+    bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
+
+    BigInteger hS(1);
+    hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
+
+    BigInteger delta(0);
+    dS.Difference(bS, &delta);
+
+    return delta.Compare(hS);
+}
+
+inline bool StrtodFast(double d, int p, double* result) {
+    // Use fast path for string-to-double conversion if possible
+    // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
+    if (p > 22  && p < 22 + 16) {
+        // Fast Path Cases In Disguise
+        d *= internal::Pow10(p - 22);
+        p = 22;
+    }
+
+    if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
+        *result = FastPath(d, p);
+        return true;
+    }
+    else
+        return false;
+}
+
+// Compute an approximation and see if it is within 1/2 ULP
+inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
+    uint64_t significand = 0;
+    size_t i = 0;   // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999    
+    for (; i < length; i++) {
+        if (significand  >  RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
+            (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
+            break;
+        significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
+    }
+    
+    if (i < length && decimals[i] >= '5') // Rounding
+        significand++;
+
+    size_t remaining = length - i;
+    const int kUlpShift = 3;
+    const int kUlp = 1 << kUlpShift;
+    int64_t error = (remaining == 0) ? 0 : kUlp / 2;
+
+    DiyFp v(significand, 0);
+    v = v.Normalize();
+    error <<= -v.e;
+
+    const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
+
+    int actualExp;
+    DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
+    if (actualExp != dExp) {
+        static const DiyFp kPow10[] = {
+            DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60),  // 10^1
+            DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57),  // 10^2
+            DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54),  // 10^3
+            DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50),  // 10^4
+            DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47),  // 10^5
+            DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44),  // 10^6
+            DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40)   // 10^7
+        };
+        int  adjustment = dExp - actualExp - 1;
+        RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
+        v = v * kPow10[adjustment];
+        if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
+            error += kUlp / 2;
+    }
+
+    v = v * cachedPower;
+
+    error += kUlp + (error == 0 ? 0 : 1);
+
+    const int oldExp = v.e;
+    v = v.Normalize();
+    error <<= oldExp - v.e;
+
+    const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
+    int precisionSize = 64 - effectiveSignificandSize;
+    if (precisionSize + kUlpShift >= 64) {
+        int scaleExp = (precisionSize + kUlpShift) - 63;
+        v.f >>= scaleExp;
+        v.e += scaleExp; 
+        error = (error >> scaleExp) + 1 + kUlp;
+        precisionSize -= scaleExp;
+    }
+
+    DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
+    const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
+    const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
+    if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
+        rounded.f++;
+        if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
+            rounded.f >>= 1;
+            rounded.e++;
+        }
+    }
+
+    *result = rounded.ToDouble();
+
+    return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
+}
+
+inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+    const BigInteger dInt(decimals, length);
+    const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
+    Double a(approx);
+    int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
+    if (cmp < 0)
+        return a.Value();  // within half ULP
+    else if (cmp == 0) {
+        // Round towards even
+        if (a.Significand() & 1)
+            return a.NextPositiveDouble();
+        else
+            return a.Value();
+    }
+    else // adjustment
+        return a.NextPositiveDouble();
+}
+
+inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+    RAPIDJSON_ASSERT(d >= 0.0);
+    RAPIDJSON_ASSERT(length >= 1);
+
+    double result;
+    if (StrtodFast(d, p, &result))
+        return result;
+
+    // Trim leading zeros
+    while (*decimals == '0' && length > 1) {
+        length--;
+        decimals++;
+        decimalPosition--;
+    }
+
+    // Trim trailing zeros
+    while (decimals[length - 1] == '0' && length > 1) {
+        length--;
+        decimalPosition--;
+        exp++;
+    }
+
+    // Trim right-most digits
+    const int kMaxDecimalDigit = 780;
+    if (static_cast<int>(length) > kMaxDecimalDigit) {
+        int delta = (static_cast<int>(length) - kMaxDecimalDigit);
+        exp += delta;
+        decimalPosition -= static_cast<unsigned>(delta);
+        length = kMaxDecimalDigit;
+    }
+
+    // If too small, underflow to zero
+    if (int(length) + exp < -324)
+        return 0.0;
+
+    if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
+        return result;
+
+    // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
+    return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STRTOD_
diff --git a/src/lottie/rapidjson/internal/swap.h b/src/lottie/rapidjson/internal/swap.h
new file mode 100644 (file)
index 0000000..666e49f
--- /dev/null
@@ -0,0 +1,46 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_SWAP_H_
+#define RAPIDJSON_INTERNAL_SWAP_H_
+
+#include "../rapidjson.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Custom swap() to avoid dependency on C++ <algorithm> header
+/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
+    \note This has the same semantics as std::swap().
+*/
+template <typename T>
+inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
+    T tmp = a;
+        a = b;
+        b = tmp;
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_SWAP_H_
diff --git a/src/lottie/rapidjson/istreamwrapper.h b/src/lottie/rapidjson/istreamwrapper.h
new file mode 100644 (file)
index 0000000..8639c8c
--- /dev/null
@@ -0,0 +1,115 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ISTREAMWRAPPER_H_
+#define RAPIDJSON_ISTREAMWRAPPER_H_
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
+/*!
+    The classes can be wrapped including but not limited to:
+
+    - \c std::istringstream
+    - \c std::stringstream
+    - \c std::wistringstream
+    - \c std::wstringstream
+    - \c std::ifstream
+    - \c std::fstream
+    - \c std::wifstream
+    - \c std::wfstream
+
+    \tparam StreamType Class derived from \c std::basic_istream.
+*/
+   
+template <typename StreamType>
+class BasicIStreamWrapper {
+public:
+    typedef typename StreamType::char_type Ch;
+    BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {}
+
+    Ch Peek() const { 
+        typename StreamType::int_type c = stream_.peek();
+        return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : static_cast<Ch>('\0');
+    }
+
+    Ch Take() { 
+        typename StreamType::int_type c = stream_.get();
+        if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) {
+            count_++;
+            return static_cast<Ch>(c);
+        }
+        else
+            return '\0';
+    }
+
+    // tellg() may return -1 when failed. So we count by ourself.
+    size_t Tell() const { return count_; }
+
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    void Put(Ch) { RAPIDJSON_ASSERT(false); }
+    void Flush() { RAPIDJSON_ASSERT(false); }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+    // For encoding detection only.
+    const Ch* Peek4() const {
+        RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream.
+        int i;
+        bool hasError = false;
+        for (i = 0; i < 4; ++i) {
+            typename StreamType::int_type c = stream_.get();
+            if (c == StreamType::traits_type::eof()) {
+                hasError = true;
+                stream_.clear();
+                break;
+            }
+            peekBuffer_[i] = static_cast<Ch>(c);
+        }
+        for (--i; i >= 0; --i)
+            stream_.putback(peekBuffer_[i]);
+        return !hasError ? peekBuffer_ : 0;
+    }
+
+private:
+    BasicIStreamWrapper(const BasicIStreamWrapper&);
+    BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
+
+    StreamType& stream_;
+    size_t count_;  //!< Number of characters read. Note:
+    mutable Ch peekBuffer_[4];
+};
+
+typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
+typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
+
+#if defined(__clang__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ISTREAMWRAPPER_H_
diff --git a/src/lottie/rapidjson/memorybuffer.h b/src/lottie/rapidjson/memorybuffer.h
new file mode 100644 (file)
index 0000000..39bee1d
--- /dev/null
@@ -0,0 +1,70 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_MEMORYBUFFER_H_
+#define RAPIDJSON_MEMORYBUFFER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory output byte stream.
+/*!
+    This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
+
+    It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
+
+    Differences between MemoryBuffer and StringBuffer:
+    1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. 
+    2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
+
+    \tparam Allocator type for allocating memory buffer.
+    \note implements Stream concept
+*/
+template <typename Allocator = CrtAllocator>
+struct GenericMemoryBuffer {
+    typedef char Ch; // byte
+
+    GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
+
+    void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+    void Flush() {}
+
+    void Clear() { stack_.Clear(); }
+    void ShrinkToFit() { stack_.ShrinkToFit(); }
+    Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+    void Pop(size_t count) { stack_.template Pop<Ch>(count); }
+
+    const Ch* GetBuffer() const {
+        return stack_.template Bottom<Ch>();
+    }
+
+    size_t GetSize() const { return stack_.GetSize(); }
+
+    static const size_t kDefaultCapacity = 256;
+    mutable internal::Stack<Allocator> stack_;
+};
+
+typedef GenericMemoryBuffer<> MemoryBuffer;
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
+    std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_MEMORYBUFFER_H_
diff --git a/src/lottie/rapidjson/memorystream.h b/src/lottie/rapidjson/memorystream.h
new file mode 100644 (file)
index 0000000..1d71d8a
--- /dev/null
@@ -0,0 +1,71 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_MEMORYSTREAM_H_
+#define RAPIDJSON_MEMORYSTREAM_H_
+
+#include "stream.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory input byte stream.
+/*!
+    This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
+
+    It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
+
+    Differences between MemoryStream and StringStream:
+    1. StringStream has encoding but MemoryStream is a byte stream.
+    2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
+    3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
+    \note implements Stream concept
+*/
+struct MemoryStream {
+    typedef char Ch; // byte
+
+    MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
+
+    Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
+    Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
+    size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
+
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    void Put(Ch) { RAPIDJSON_ASSERT(false); }
+    void Flush() { RAPIDJSON_ASSERT(false); }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+    // For encoding detection only.
+    const Ch* Peek4() const {
+        return Tell() + 4 <= size_ ? src_ : 0;
+    }
+
+    const Ch* src_;     //!< Current read position.
+    const Ch* begin_;   //!< Original head of the string.
+    const Ch* end_;     //!< End of stream.
+    size_t size_;       //!< Size of the stream.
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_MEMORYBUFFER_H_
diff --git a/src/lottie/rapidjson/meson.build b/src/lottie/rapidjson/meson.build
new file mode 100644 (file)
index 0000000..2809bab
--- /dev/null
@@ -0,0 +1,8 @@
+ssg_sources_lottie_rapidjson = []
+
+ssg_lottie_rapidjson_file = ['jsontest.cpp'
+                            ]
+
+foreach file: ssg_lottie_rapidjson_file
+   ssg_sources_lottie_rapidjson += join_paths('rapidjson', file)
+endforeach
diff --git a/src/lottie/rapidjson/msinttypes/inttypes.h b/src/lottie/rapidjson/msinttypes/inttypes.h
new file mode 100644 (file)
index 0000000..1811128
--- /dev/null
@@ -0,0 +1,316 @@
+// ISO C9x  compliant inttypes.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2013 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of the product nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+// The above software in this distribution may have been modified by 
+// THL A29 Limited ("Tencent Modifications"). 
+// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_INTTYPES_H_ // [
+#define _MSC_INTTYPES_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "stdint.h"
+
+// miloyip: VC supports inttypes.h since VC2013
+#if _MSC_VER >= 1800
+#include <inttypes.h>
+#else
+
+// 7.8 Format conversion of integer types
+
+typedef struct {
+   intmax_t quot;
+   intmax_t rem;
+} imaxdiv_t;
+
+// 7.8.1 Macros for format specifiers
+
+#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [   See footnote 185 at page 198
+
+// The fprintf macros for signed integers are:
+#define PRId8       "d"
+#define PRIi8       "i"
+#define PRIdLEAST8  "d"
+#define PRIiLEAST8  "i"
+#define PRIdFAST8   "d"
+#define PRIiFAST8   "i"
+
+#define PRId16       "hd"
+#define PRIi16       "hi"
+#define PRIdLEAST16  "hd"
+#define PRIiLEAST16  "hi"
+#define PRIdFAST16   "hd"
+#define PRIiFAST16   "hi"
+
+#define PRId32       "I32d"
+#define PRIi32       "I32i"
+#define PRIdLEAST32  "I32d"
+#define PRIiLEAST32  "I32i"
+#define PRIdFAST32   "I32d"
+#define PRIiFAST32   "I32i"
+
+#define PRId64       "I64d"
+#define PRIi64       "I64i"
+#define PRIdLEAST64  "I64d"
+#define PRIiLEAST64  "I64i"
+#define PRIdFAST64   "I64d"
+#define PRIiFAST64   "I64i"
+
+#define PRIdMAX     "I64d"
+#define PRIiMAX     "I64i"
+
+#define PRIdPTR     "Id"
+#define PRIiPTR     "Ii"
+
+// The fprintf macros for unsigned integers are:
+#define PRIo8       "o"
+#define PRIu8       "u"
+#define PRIx8       "x"
+#define PRIX8       "X"
+#define PRIoLEAST8  "o"
+#define PRIuLEAST8  "u"
+#define PRIxLEAST8  "x"
+#define PRIXLEAST8  "X"
+#define PRIoFAST8   "o"
+#define PRIuFAST8   "u"
+#define PRIxFAST8   "x"
+#define PRIXFAST8   "X"
+
+#define PRIo16       "ho"
+#define PRIu16       "hu"
+#define PRIx16       "hx"
+#define PRIX16       "hX"
+#define PRIoLEAST16  "ho"
+#define PRIuLEAST16  "hu"
+#define PRIxLEAST16  "hx"
+#define PRIXLEAST16  "hX"
+#define PRIoFAST16   "ho"
+#define PRIuFAST16   "hu"
+#define PRIxFAST16   "hx"
+#define PRIXFAST16   "hX"
+
+#define PRIo32       "I32o"
+#define PRIu32       "I32u"
+#define PRIx32       "I32x"
+#define PRIX32       "I32X"
+#define PRIoLEAST32  "I32o"
+#define PRIuLEAST32  "I32u"
+#define PRIxLEAST32  "I32x"
+#define PRIXLEAST32  "I32X"
+#define PRIoFAST32   "I32o"
+#define PRIuFAST32   "I32u"
+#define PRIxFAST32   "I32x"
+#define PRIXFAST32   "I32X"
+
+#define PRIo64       "I64o"
+#define PRIu64       "I64u"
+#define PRIx64       "I64x"
+#define PRIX64       "I64X"
+#define PRIoLEAST64  "I64o"
+#define PRIuLEAST64  "I64u"
+#define PRIxLEAST64  "I64x"
+#define PRIXLEAST64  "I64X"
+#define PRIoFAST64   "I64o"
+#define PRIuFAST64   "I64u"
+#define PRIxFAST64   "I64x"
+#define PRIXFAST64   "I64X"
+
+#define PRIoMAX     "I64o"
+#define PRIuMAX     "I64u"
+#define PRIxMAX     "I64x"
+#define PRIXMAX     "I64X"
+
+#define PRIoPTR     "Io"
+#define PRIuPTR     "Iu"
+#define PRIxPTR     "Ix"
+#define PRIXPTR     "IX"
+
+// The fscanf macros for signed integers are:
+#define SCNd8       "d"
+#define SCNi8       "i"
+#define SCNdLEAST8  "d"
+#define SCNiLEAST8  "i"
+#define SCNdFAST8   "d"
+#define SCNiFAST8   "i"
+
+#define SCNd16       "hd"
+#define SCNi16       "hi"
+#define SCNdLEAST16  "hd"
+#define SCNiLEAST16  "hi"
+#define SCNdFAST16   "hd"
+#define SCNiFAST16   "hi"
+
+#define SCNd32       "ld"
+#define SCNi32       "li"
+#define SCNdLEAST32  "ld"
+#define SCNiLEAST32  "li"
+#define SCNdFAST32   "ld"
+#define SCNiFAST32   "li"
+
+#define SCNd64       "I64d"
+#define SCNi64       "I64i"
+#define SCNdLEAST64  "I64d"
+#define SCNiLEAST64  "I64i"
+#define SCNdFAST64   "I64d"
+#define SCNiFAST64   "I64i"
+
+#define SCNdMAX     "I64d"
+#define SCNiMAX     "I64i"
+
+#ifdef _WIN64 // [
+#  define SCNdPTR     "I64d"
+#  define SCNiPTR     "I64i"
+#else  // _WIN64 ][
+#  define SCNdPTR     "ld"
+#  define SCNiPTR     "li"
+#endif  // _WIN64 ]
+
+// The fscanf macros for unsigned integers are:
+#define SCNo8       "o"
+#define SCNu8       "u"
+#define SCNx8       "x"
+#define SCNX8       "X"
+#define SCNoLEAST8  "o"
+#define SCNuLEAST8  "u"
+#define SCNxLEAST8  "x"
+#define SCNXLEAST8  "X"
+#define SCNoFAST8   "o"
+#define SCNuFAST8   "u"
+#define SCNxFAST8   "x"
+#define SCNXFAST8   "X"
+
+#define SCNo16       "ho"
+#define SCNu16       "hu"
+#define SCNx16       "hx"
+#define SCNX16       "hX"
+#define SCNoLEAST16  "ho"
+#define SCNuLEAST16  "hu"
+#define SCNxLEAST16  "hx"
+#define SCNXLEAST16  "hX"
+#define SCNoFAST16   "ho"
+#define SCNuFAST16   "hu"
+#define SCNxFAST16   "hx"
+#define SCNXFAST16   "hX"
+
+#define SCNo32       "lo"
+#define SCNu32       "lu"
+#define SCNx32       "lx"
+#define SCNX32       "lX"
+#define SCNoLEAST32  "lo"
+#define SCNuLEAST32  "lu"
+#define SCNxLEAST32  "lx"
+#define SCNXLEAST32  "lX"
+#define SCNoFAST32   "lo"
+#define SCNuFAST32   "lu"
+#define SCNxFAST32   "lx"
+#define SCNXFAST32   "lX"
+
+#define SCNo64       "I64o"
+#define SCNu64       "I64u"
+#define SCNx64       "I64x"
+#define SCNX64       "I64X"
+#define SCNoLEAST64  "I64o"
+#define SCNuLEAST64  "I64u"
+#define SCNxLEAST64  "I64x"
+#define SCNXLEAST64  "I64X"
+#define SCNoFAST64   "I64o"
+#define SCNuFAST64   "I64u"
+#define SCNxFAST64   "I64x"
+#define SCNXFAST64   "I64X"
+
+#define SCNoMAX     "I64o"
+#define SCNuMAX     "I64u"
+#define SCNxMAX     "I64x"
+#define SCNXMAX     "I64X"
+
+#ifdef _WIN64 // [
+#  define SCNoPTR     "I64o"
+#  define SCNuPTR     "I64u"
+#  define SCNxPTR     "I64x"
+#  define SCNXPTR     "I64X"
+#else  // _WIN64 ][
+#  define SCNoPTR     "lo"
+#  define SCNuPTR     "lu"
+#  define SCNxPTR     "lx"
+#  define SCNXPTR     "lX"
+#endif  // _WIN64 ]
+
+#endif // __STDC_FORMAT_MACROS ]
+
+// 7.8.2 Functions for greatest-width integer types
+
+// 7.8.2.1 The imaxabs function
+#define imaxabs _abs64
+
+// 7.8.2.2 The imaxdiv function
+
+// This is modified version of div() function from Microsoft's div.c found
+// in %MSVC.NET%\crt\src\div.c
+#ifdef STATIC_IMAXDIV // [
+static
+#else // STATIC_IMAXDIV ][
+_inline
+#endif // STATIC_IMAXDIV ]
+imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
+{
+   imaxdiv_t result;
+
+   result.quot = numer / denom;
+   result.rem = numer % denom;
+
+   if (numer < 0 && result.rem > 0) {
+      // did division wrong; must fix up
+      ++result.quot;
+      result.rem -= denom;
+   }
+
+   return result;
+}
+
+// 7.8.2.3 The strtoimax and strtoumax functions
+#define strtoimax _strtoi64
+#define strtoumax _strtoui64
+
+// 7.8.2.4 The wcstoimax and wcstoumax functions
+#define wcstoimax _wcstoi64
+#define wcstoumax _wcstoui64
+
+#endif // _MSC_VER >= 1800
+
+#endif // _MSC_INTTYPES_H_ ]
diff --git a/src/lottie/rapidjson/msinttypes/stdint.h b/src/lottie/rapidjson/msinttypes/stdint.h
new file mode 100644 (file)
index 0000000..3d4477b
--- /dev/null
@@ -0,0 +1,300 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2013 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of the product nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+// The above software in this distribution may have been modified by 
+// THL A29 Limited ("Tencent Modifications"). 
+// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
+#if _MSC_VER >= 1600 // [
+#include <stdint.h>
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+#undef INT8_C
+#undef INT16_C
+#undef INT32_C
+#undef INT64_C
+#undef UINT8_C
+#undef UINT16_C
+#undef UINT32_C
+#undef UINT64_C
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C //   [
+#  define INTMAX_C   INT64_C
+#endif // INTMAX_C    ]
+#ifndef UINTMAX_C //  [
+#  define UINTMAX_C  UINT64_C
+#endif // UINTMAX_C   ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#else // ] _MSC_VER >= 1700 [
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler would give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#if defined(__cplusplus) && !defined(_M_ARM)
+extern "C" {
+#endif
+#  include <wchar.h>
+#if defined(__cplusplus) && !defined(_M_ARM)
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef signed char       int8_t;
+   typedef signed short      int16_t;
+   typedef signed int        int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef signed __int16    int16_t;
+   typedef signed __int32    int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef signed __int64       int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef signed __int64    intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 signed int   intptr_t;
+   typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C //   [
+#  define INTMAX_C   INT64_C
+#endif // INTMAX_C    ]
+#ifndef UINTMAX_C //  [
+#  define UINTMAX_C  UINT64_C
+#endif // UINTMAX_C   ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_VER >= 1600 ]
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/src/lottie/rapidjson/ostreamwrapper.h b/src/lottie/rapidjson/ostreamwrapper.h
new file mode 100644 (file)
index 0000000..6f4667c
--- /dev/null
@@ -0,0 +1,81 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_OSTREAMWRAPPER_H_
+#define RAPIDJSON_OSTREAMWRAPPER_H_
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
+/*!
+    The classes can be wrapped including but not limited to:
+
+    - \c std::ostringstream
+    - \c std::stringstream
+    - \c std::wpstringstream
+    - \c std::wstringstream
+    - \c std::ifstream
+    - \c std::fstream
+    - \c std::wofstream
+    - \c std::wfstream
+
+    \tparam StreamType Class derived from \c std::basic_ostream.
+*/
+   
+template <typename StreamType>
+class BasicOStreamWrapper {
+public:
+    typedef typename StreamType::char_type Ch;
+    BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
+
+    void Put(Ch c) {
+        stream_.put(c);
+    }
+
+    void Flush() {
+        stream_.flush();
+    }
+
+    // Not implemented
+    char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+    char Take() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+    char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+    BasicOStreamWrapper(const BasicOStreamWrapper&);
+    BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
+
+    StreamType& stream_;
+};
+
+typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
+typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_OSTREAMWRAPPER_H_
diff --git a/src/lottie/rapidjson/pointer.h b/src/lottie/rapidjson/pointer.h
new file mode 100644 (file)
index 0000000..0f377ef
--- /dev/null
@@ -0,0 +1,1358 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_POINTER_H_
+#define RAPIDJSON_POINTER_H_
+
+#include "document.h"
+#include "internal/itoa.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(switch-enum)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+static const SizeType kPointerInvalidIndex = ~SizeType(0);  //!< Represents an invalid index in GenericPointer::Token
+
+//! Error code of parsing.
+/*! \ingroup RAPIDJSON_ERRORS
+    \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
+*/
+enum PointerParseErrorCode {
+    kPointerParseErrorNone = 0,                     //!< The parse is successful
+
+    kPointerParseErrorTokenMustBeginWithSolidus,    //!< A token must begin with a '/'
+    kPointerParseErrorInvalidEscape,                //!< Invalid escape
+    kPointerParseErrorInvalidPercentEncoding,       //!< Invalid percent encoding in URI fragment
+    kPointerParseErrorCharacterMustPercentEncode    //!< A character must percent encoded in URI fragment
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericPointer
+
+//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
+/*!
+    This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" 
+    (https://tools.ietf.org/html/rfc6901).
+
+    A JSON pointer is for identifying a specific value in a JSON document
+    (GenericDocument). It can simplify coding of DOM tree manipulation, because it
+    can access multiple-level depth of DOM tree with single API call.
+
+    After it parses a string representation (e.g. "/foo/0" or URI fragment 
+    representation (e.g. "#/foo/0") into its internal representation (tokens),
+    it can be used to resolve a specific value in multiple documents, or sub-tree 
+    of documents.
+
+    Contrary to GenericValue, Pointer can be copy constructed and copy assigned.
+    Apart from assignment, a Pointer cannot be modified after construction.
+
+    Although Pointer is very convenient, please aware that constructing Pointer
+    involves parsing and dynamic memory allocation. A special constructor with user-
+    supplied tokens eliminates these.
+
+    GenericPointer depends on GenericDocument and GenericValue.
+    
+    \tparam ValueType The value type of the DOM tree. E.g. GenericValue<UTF8<> >
+    \tparam Allocator The allocator type for allocating memory for internal representation.
+    
+    \note GenericPointer uses same encoding of ValueType.
+    However, Allocator of GenericPointer is independent of Allocator of Value.
+*/
+template <typename ValueType, typename Allocator = CrtAllocator>
+class GenericPointer {
+public:
+    typedef typename ValueType::EncodingType EncodingType;  //!< Encoding type from Value
+    typedef typename ValueType::Ch Ch;                      //!< Character type from Value
+
+    //! A token is the basic units of internal representation.
+    /*!
+        A JSON pointer string representation "/foo/123" is parsed to two tokens: 
+        "foo" and 123. 123 will be represented in both numeric form and string form.
+        They are resolved according to the actual value type (object or array).
+
+        For token that are not numbers, or the numeric value is out of bound
+        (greater than limits of SizeType), they are only treated as string form
+        (i.e. the token's index will be equal to kPointerInvalidIndex).
+
+        This struct is public so that user can create a Pointer without parsing and 
+        allocation, using a special constructor.
+    */
+    struct Token {
+        const Ch* name;             //!< Name of the token. It has null character at the end but it can contain null character.
+        SizeType length;            //!< Length of the name.
+        SizeType index;             //!< A valid array index, if it is not equal to kPointerInvalidIndex.
+    };
+
+    //!@name Constructors and destructor.
+    //@{
+
+    //! Default constructor.
+    GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+
+    //! Constructor that parses a string or URI fragment representation.
+    /*!
+        \param source A null-terminated, string or URI fragment representation of JSON pointer.
+        \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+    */
+    explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+        Parse(source, internal::StrLen(source));
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Constructor that parses a string or URI fragment representation.
+    /*!
+        \param source A string or URI fragment representation of JSON pointer.
+        \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+        \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+    */
+    explicit GenericPointer(const std::basic_string<Ch>& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+        Parse(source.c_str(), source.size());
+    }
+#endif
+
+    //! Constructor that parses a string or URI fragment representation, with length of the source string.
+    /*!
+        \param source A string or URI fragment representation of JSON pointer.
+        \param length Length of source.
+        \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+        \note Slightly faster than the overload without length.
+    */
+    GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+        Parse(source, length);
+    }
+
+    //! Constructor with user-supplied tokens.
+    /*!
+        This constructor let user supplies const array of tokens.
+        This prevents the parsing process and eliminates allocation.
+        This is preferred for memory constrained environments.
+
+        \param tokens An constant array of tokens representing the JSON pointer.
+        \param tokenCount Number of tokens.
+
+        \b Example
+        \code
+        #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
+        #define INDEX(i) { #i, sizeof(#i) - 1, i }
+
+        static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) };
+        static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+        // Equivalent to static const Pointer p("/foo/123");
+
+        #undef NAME
+        #undef INDEX
+        \endcode
+    */
+    GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+
+    //! Copy constructor.
+    GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+        *this = rhs;
+    }
+
+    //! Destructor.
+    ~GenericPointer() {
+        if (nameBuffer_)    // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated.
+            Allocator::Free(tokens_);
+        RAPIDJSON_DELETE(ownAllocator_);
+    }
+
+    //! Assignment operator.
+    GenericPointer& operator=(const GenericPointer& rhs) {
+        if (this != &rhs) {
+            // Do not delete ownAllcator
+            if (nameBuffer_)
+                Allocator::Free(tokens_);
+
+            tokenCount_ = rhs.tokenCount_;
+            parseErrorOffset_ = rhs.parseErrorOffset_;
+            parseErrorCode_ = rhs.parseErrorCode_;
+
+            if (rhs.nameBuffer_)
+                CopyFromRaw(rhs); // Normally parsed tokens.
+            else {
+                tokens_ = rhs.tokens_; // User supplied const tokens.
+                nameBuffer_ = 0;
+            }
+        }
+        return *this;
+    }
+
+    //@}
+
+    //!@name Append token
+    //@{
+
+    //! Append a token and return a new Pointer
+    /*!
+        \param token Token to be appended.
+        \param allocator Allocator for the newly return Pointer.
+        \return A new Pointer with appended token.
+    */
+    GenericPointer Append(const Token& token, Allocator* allocator = 0) const {
+        GenericPointer r;
+        r.allocator_ = allocator;
+        Ch *p = r.CopyFromRaw(*this, 1, token.length + 1);
+        std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch));
+        r.tokens_[tokenCount_].name = p;
+        r.tokens_[tokenCount_].length = token.length;
+        r.tokens_[tokenCount_].index = token.index;
+        return r;
+    }
+
+    //! Append a name token with length, and return a new Pointer
+    /*!
+        \param name Name to be appended.
+        \param length Length of name.
+        \param allocator Allocator for the newly return Pointer.
+        \return A new Pointer with appended token.
+    */
+    GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const {
+        Token token = { name, length, kPointerInvalidIndex };
+        return Append(token, allocator);
+    }
+
+    //! Append a name token without length, and return a new Pointer
+    /*!
+        \param name Name (const Ch*) to be appended.
+        \param allocator Allocator for the newly return Pointer.
+        \return A new Pointer with appended token.
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >), (GenericPointer))
+    Append(T* name, Allocator* allocator = 0) const {
+        return Append(name, internal::StrLen(name), allocator);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Append a name token, and return a new Pointer
+    /*!
+        \param name Name to be appended.
+        \param allocator Allocator for the newly return Pointer.
+        \return A new Pointer with appended token.
+    */
+    GenericPointer Append(const std::basic_string<Ch>& name, Allocator* allocator = 0) const {
+        return Append(name.c_str(), static_cast<SizeType>(name.size()), allocator);
+    }
+#endif
+
+    //! Append a index token, and return a new Pointer
+    /*!
+        \param index Index to be appended.
+        \param allocator Allocator for the newly return Pointer.
+        \return A new Pointer with appended token.
+    */
+    GenericPointer Append(SizeType index, Allocator* allocator = 0) const {
+        char buffer[21];
+        char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer);
+        SizeType length = static_cast<SizeType>(end - buffer);
+        buffer[length] = '\0';
+
+        if (sizeof(Ch) == 1) {
+            Token token = { reinterpret_cast<Ch*>(buffer), length, index };
+            return Append(token, allocator);
+        }
+        else {
+            Ch name[21];
+            for (size_t i = 0; i <= length; i++)
+                name[i] = static_cast<Ch>(buffer[i]);
+            Token token = { name, length, index };
+            return Append(token, allocator);
+        }
+    }
+
+    //! Append a token by value, and return a new Pointer
+    /*!
+        \param token token to be appended.
+        \param allocator Allocator for the newly return Pointer.
+        \return A new Pointer with appended token.
+    */
+    GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const {
+        if (token.IsString())
+            return Append(token.GetString(), token.GetStringLength(), allocator);
+        else {
+            RAPIDJSON_ASSERT(token.IsUint64());
+            RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0));
+            return Append(static_cast<SizeType>(token.GetUint64()), allocator);
+        }
+    }
+
+    //!@name Handling Parse Error
+    //@{
+
+    //! Check whether this is a valid pointer.
+    bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; }
+
+    //! Get the parsing error offset in code unit.
+    size_t GetParseErrorOffset() const { return parseErrorOffset_; }
+
+    //! Get the parsing error code.
+    PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; }
+
+    //@}
+
+    //! Get the allocator of this pointer.
+    Allocator& GetAllocator() { return *allocator_; }
+
+    //!@name Tokens
+    //@{
+
+    //! Get the token array (const version only).
+    const Token* GetTokens() const { return tokens_; }
+
+    //! Get the number of tokens.
+    size_t GetTokenCount() const { return tokenCount_; }
+
+    //@}
+
+    //!@name Equality/inequality operators
+    //@{
+
+    //! Equality operator.
+    /*!
+        \note When any pointers are invalid, always returns false.
+    */
+    bool operator==(const GenericPointer& rhs) const {
+        if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_)
+            return false;
+
+        for (size_t i = 0; i < tokenCount_; i++) {
+            if (tokens_[i].index != rhs.tokens_[i].index ||
+                tokens_[i].length != rhs.tokens_[i].length || 
+                (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    //! Inequality operator.
+    /*!
+        \note When any pointers are invalid, always returns true.
+    */
+    bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); }
+
+    //@}
+
+    //!@name Stringify
+    //@{
+
+    //! Stringify the pointer into string representation.
+    /*!
+        \tparam OutputStream Type of output stream.
+        \param os The output stream.
+    */
+    template<typename OutputStream>
+    bool Stringify(OutputStream& os) const {
+        return Stringify<false, OutputStream>(os);
+    }
+
+    //! Stringify the pointer into URI fragment representation.
+    /*!
+        \tparam OutputStream Type of output stream.
+        \param os The output stream.
+    */
+    template<typename OutputStream>
+    bool StringifyUriFragment(OutputStream& os) const {
+        return Stringify<true, OutputStream>(os);
+    }
+
+    //@}
+
+    //!@name Create value
+    //@{
+
+    //! Create a value in a subtree.
+    /*!
+        If the value is not exist, it creates all parent values and a JSON Null value.
+        So it always succeed and return the newly created or existing value.
+
+        Remind that it may change types of parents according to tokens, so it 
+        potentially removes previously stored values. For example, if a document 
+        was an array, and "/foo" is used to create a value, then the document 
+        will be changed to an object, and all existing array elements are lost.
+
+        \param root Root value of a DOM subtree to be resolved. It can be any value other than document root.
+        \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+        \param alreadyExist If non-null, it stores whether the resolved value is already exist.
+        \return The resolved newly created (a JSON Null value), or already exists value.
+    */
+    ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const {
+        RAPIDJSON_ASSERT(IsValid());
+        ValueType* v = &root;
+        bool exist = true;
+        for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+            if (v->IsArray() && t->name[0] == '-' && t->length == 1) {
+                v->PushBack(ValueType().Move(), allocator);
+                v = &((*v)[v->Size() - 1]);
+                exist = false;
+            }
+            else {
+                if (t->index == kPointerInvalidIndex) { // must be object name
+                    if (!v->IsObject())
+                        v->SetObject(); // Change to Object
+                }
+                else { // object name or array index
+                    if (!v->IsArray() && !v->IsObject())
+                        v->SetArray(); // Change to Array
+                }
+
+                if (v->IsArray()) {
+                    if (t->index >= v->Size()) {
+                        v->Reserve(t->index + 1, allocator);
+                        while (t->index >= v->Size())
+                            v->PushBack(ValueType().Move(), allocator);
+                        exist = false;
+                    }
+                    v = &((*v)[t->index]);
+                }
+                else {
+                    typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+                    if (m == v->MemberEnd()) {
+                        v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator);
+                        v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end
+                        exist = false;
+                    }
+                    else
+                        v = &m->value;
+                }
+            }
+        }
+
+        if (alreadyExist)
+            *alreadyExist = exist;
+
+        return *v;
+    }
+
+    //! Creates a value in a document.
+    /*!
+        \param document A document to be resolved.
+        \param alreadyExist If non-null, it stores whether the resolved value is already exist.
+        \return The resolved newly created, or already exists value.
+    */
+    template <typename stackAllocator>
+    ValueType& Create(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, bool* alreadyExist = 0) const {
+        return Create(document, document.GetAllocator(), alreadyExist);
+    }
+
+    //@}
+
+    //!@name Query value
+    //@{
+
+    //! Query a value in a subtree.
+    /*!
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
+        \return Pointer to the value if it can be resolved. Otherwise null.
+
+        \note
+        There are only 3 situations when a value cannot be resolved:
+        1. A value in the path is not an array nor object.
+        2. An object value does not contain the token.
+        3. A token is out of range of an array value.
+
+        Use unresolvedTokenIndex to retrieve the token index.
+    */
+    ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const {
+        RAPIDJSON_ASSERT(IsValid());
+        ValueType* v = &root;
+        for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+            switch (v->GetType()) {
+            case kObjectType:
+                {
+                    typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+                    if (m == v->MemberEnd())
+                        break;
+                    v = &m->value;
+                }
+                continue;
+            case kArrayType:
+                if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+                    break;
+                v = &((*v)[t->index]);
+                continue;
+            default:
+                break;
+            }
+
+            // Error: unresolved token
+            if (unresolvedTokenIndex)
+                *unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
+            return 0;
+        }
+        return v;
+    }
+
+    //! Query a const value in a const subtree.
+    /*!
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \return Pointer to the value if it can be resolved. Otherwise null.
+    */
+    const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { 
+        return Get(const_cast<ValueType&>(root), unresolvedTokenIndex);
+    }
+
+    //@}
+
+    //!@name Query a value with default
+    //@{
+
+    //! Query a value in a subtree with default value.
+    /*!
+        Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value.
+        So that this function always succeed.
+
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \param defaultValue Default value to be cloned if the value was not exists.
+        \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+        \see Create()
+    */
+    ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const {
+        bool alreadyExist;
+        Value& v = Create(root, allocator, &alreadyExist);
+        return alreadyExist ? v : v.CopyFrom(defaultValue, allocator);
+    }
+
+    //! Query a value in a subtree with default null-terminated string.
+    ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const {
+        bool alreadyExist;
+        Value& v = Create(root, allocator, &alreadyExist);
+        return alreadyExist ? v : v.SetString(defaultValue, allocator);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Query a value in a subtree with default std::basic_string.
+    ValueType& GetWithDefault(ValueType& root, const std::basic_string<Ch>& defaultValue, typename ValueType::AllocatorType& allocator) const {
+        bool alreadyExist;
+        Value& v = Create(root, allocator, &alreadyExist);
+        return alreadyExist ? v : v.SetString(defaultValue, allocator);
+    }
+#endif
+
+    //! Query a value in a subtree with default primitive value.
+    /*!
+        \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+    GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const {
+        return GetWithDefault(root, ValueType(defaultValue).Move(), allocator);
+    }
+
+    //! Query a value in a document with default value.
+    template <typename stackAllocator>
+    ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& defaultValue) const {
+        return GetWithDefault(document, defaultValue, document.GetAllocator());
+    }
+
+    //! Query a value in a document with default null-terminated string.
+    template <typename stackAllocator>
+    ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* defaultValue) const {
+        return GetWithDefault(document, defaultValue, document.GetAllocator());
+    }
+    
+#if RAPIDJSON_HAS_STDSTRING
+    //! Query a value in a document with default std::basic_string.
+    template <typename stackAllocator>
+    ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& defaultValue) const {
+        return GetWithDefault(document, defaultValue, document.GetAllocator());
+    }
+#endif
+
+    //! Query a value in a document with default primitive value.
+    /*!
+        \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+    */
+    template <typename T, typename stackAllocator>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+    GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T defaultValue) const {
+        return GetWithDefault(document, defaultValue, document.GetAllocator());
+    }
+
+    //@}
+
+    //!@name Set a value
+    //@{
+
+    //! Set a value in a subtree, with move semantics.
+    /*!
+        It creates all parents if they are not exist or types are different to the tokens.
+        So this function always succeeds but potentially remove existing values.
+
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \param value Value to be set.
+        \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+        \see Create()
+    */
+    ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const {
+        return Create(root, allocator) = value;
+    }
+
+    //! Set a value in a subtree, with copy semantics.
+    ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const {
+        return Create(root, allocator).CopyFrom(value, allocator);
+    }
+
+    //! Set a null-terminated string in a subtree.
+    ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const {
+        return Create(root, allocator) = ValueType(value, allocator).Move();
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Set a std::basic_string in a subtree.
+    ValueType& Set(ValueType& root, const std::basic_string<Ch>& value, typename ValueType::AllocatorType& allocator) const {
+        return Create(root, allocator) = ValueType(value, allocator).Move();
+    }
+#endif
+
+    //! Set a primitive value in a subtree.
+    /*!
+        \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+    */
+    template <typename T>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+    Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const {
+        return Create(root, allocator) = ValueType(value).Move();
+    }
+
+    //! Set a value in a document, with move semantics.
+    template <typename stackAllocator>
+    ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const {
+        return Create(document) = value;
+    }
+
+    //! Set a value in a document, with copy semantics.
+    template <typename stackAllocator>
+    ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& value) const {
+        return Create(document).CopyFrom(value, document.GetAllocator());
+    }
+
+    //! Set a null-terminated string in a document.
+    template <typename stackAllocator>
+    ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* value) const {
+        return Create(document) = ValueType(value, document.GetAllocator()).Move();
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    //! Sets a std::basic_string in a document.
+    template <typename stackAllocator>
+    ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& value) const {
+        return Create(document) = ValueType(value, document.GetAllocator()).Move();
+    }
+#endif
+
+    //! Set a primitive value in a document.
+    /*!
+    \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+    */
+    template <typename T, typename stackAllocator>
+    RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+        Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T value) const {
+            return Create(document) = value;
+    }
+
+    //@}
+
+    //!@name Swap a value
+    //@{
+
+    //! Swap a value with a value in a subtree.
+    /*!
+        It creates all parents if they are not exist or types are different to the tokens.
+        So this function always succeeds but potentially remove existing values.
+
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \param value Value to be swapped.
+        \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+        \see Create()
+    */
+    ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const {
+        return Create(root, allocator).Swap(value);
+    }
+
+    //! Swap a value with a value in a document.
+    template <typename stackAllocator>
+    ValueType& Swap(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const {
+        return Create(document).Swap(value);
+    }
+
+    //@}
+
+    //! Erase a value in a subtree.
+    /*!
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \return Whether the resolved value is found and erased.
+
+        \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false.
+    */
+    bool Erase(ValueType& root) const {
+        RAPIDJSON_ASSERT(IsValid());
+        if (tokenCount_ == 0) // Cannot erase the root
+            return false;
+
+        ValueType* v = &root;
+        const Token* last = tokens_ + (tokenCount_ - 1);
+        for (const Token *t = tokens_; t != last; ++t) {
+            switch (v->GetType()) {
+            case kObjectType:
+                {
+                    typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+                    if (m == v->MemberEnd())
+                        return false;
+                    v = &m->value;
+                }
+                break;
+            case kArrayType:
+                if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+                    return false;
+                v = &((*v)[t->index]);
+                break;
+            default:
+                return false;
+            }
+        }
+
+        switch (v->GetType()) {
+        case kObjectType:
+            return v->EraseMember(GenericStringRef<Ch>(last->name, last->length));
+        case kArrayType:
+            if (last->index == kPointerInvalidIndex || last->index >= v->Size())
+                return false;
+            v->Erase(v->Begin() + last->index);
+            return true;
+        default:
+            return false;
+        }
+    }
+
+private:
+    //! Clone the content from rhs to this.
+    /*!
+        \param rhs Source pointer.
+        \param extraToken Extra tokens to be allocated.
+        \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated.
+        \return Start of non-occupied name buffer, for storing extra names.
+    */
+    Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) {
+        if (!allocator_) // allocator is independently owned.
+            ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+
+        size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens
+        for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t)
+            nameBufferSize += t->length;
+
+        tokenCount_ = rhs.tokenCount_ + extraToken;
+        tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch)));
+        nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
+        if (rhs.tokenCount_ > 0) {
+            std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token));
+        }
+        if (nameBufferSize > 0) {
+            std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
+        }
+
+        // Adjust pointers to name buffer
+        std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_;
+        for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t)
+            t->name += diff;
+
+        return nameBuffer_ + nameBufferSize;
+    }
+
+    //! Check whether a character should be percent-encoded.
+    /*!
+        According to RFC 3986 2.3 Unreserved Characters.
+        \param c The character (code unit) to be tested.
+    */
+    bool NeedPercentEncode(Ch c) const {
+        return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~');
+    }
+
+    //! Parse a JSON String or its URI fragment representation into tokens.
+#ifndef __clang__ // -Wdocumentation
+    /*!
+        \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated.
+        \param length Length of the source string.
+        \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped.
+    */
+#endif
+    void Parse(const Ch* source, size_t length) {
+        RAPIDJSON_ASSERT(source != NULL);
+        RAPIDJSON_ASSERT(nameBuffer_ == 0);
+        RAPIDJSON_ASSERT(tokens_ == 0);
+
+        // Create own allocator if user did not supply.
+        if (!allocator_)
+            ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+
+        // Count number of '/' as tokenCount
+        tokenCount_ = 0;
+        for (const Ch* s = source; s != source + length; s++) 
+            if (*s == '/')
+                tokenCount_++;
+
+        Token* token = tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch)));
+        Ch* name = nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
+        size_t i = 0;
+
+        // Detect if it is a URI fragment
+        bool uriFragment = false;
+        if (source[i] == '#') {
+            uriFragment = true;
+            i++;
+        }
+
+        if (i != length && source[i] != '/') {
+            parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus;
+            goto error;
+        }
+
+        while (i < length) {
+            RAPIDJSON_ASSERT(source[i] == '/');
+            i++; // consumes '/'
+
+            token->name = name;
+            bool isNumber = true;
+
+            while (i < length && source[i] != '/') {
+                Ch c = source[i];
+                if (uriFragment) {
+                    // Decoding percent-encoding for URI fragment
+                    if (c == '%') {
+                        PercentDecodeStream is(&source[i], source + length);
+                        GenericInsituStringStream<EncodingType> os(name);
+                        Ch* begin = os.PutBegin();
+                        if (!Transcoder<UTF8<>, EncodingType>().Validate(is, os) || !is.IsValid()) {
+                            parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding;
+                            goto error;
+                        }
+                        size_t len = os.PutEnd(begin);
+                        i += is.Tell() - 1;
+                        if (len == 1)
+                            c = *name;
+                        else {
+                            name += len;
+                            isNumber = false;
+                            i++;
+                            continue;
+                        }
+                    }
+                    else if (NeedPercentEncode(c)) {
+                        parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode;
+                        goto error;
+                    }
+                }
+
+                i++;
+                
+                // Escaping "~0" -> '~', "~1" -> '/'
+                if (c == '~') {
+                    if (i < length) {
+                        c = source[i];
+                        if (c == '0')       c = '~';
+                        else if (c == '1')  c = '/';
+                        else {
+                            parseErrorCode_ = kPointerParseErrorInvalidEscape;
+                            goto error;
+                        }
+                        i++;
+                    }
+                    else {
+                        parseErrorCode_ = kPointerParseErrorInvalidEscape;
+                        goto error;
+                    }
+                }
+
+                // First check for index: all of characters are digit
+                if (c < '0' || c > '9')
+                    isNumber = false;
+
+                *name++ = c;
+            }
+            token->length = static_cast<SizeType>(name - token->name);
+            if (token->length == 0)
+                isNumber = false;
+            *name++ = '\0'; // Null terminator
+
+            // Second check for index: more than one digit cannot have leading zero
+            if (isNumber && token->length > 1 && token->name[0] == '0')
+                isNumber = false;
+
+            // String to SizeType conversion
+            SizeType n = 0;
+            if (isNumber) {
+                for (size_t j = 0; j < token->length; j++) {
+                    SizeType m = n * 10 + static_cast<SizeType>(token->name[j] - '0');
+                    if (m < n) {   // overflow detection
+                        isNumber = false;
+                        break;
+                    }
+                    n = m;
+                }
+            }
+
+            token->index = isNumber ? n : kPointerInvalidIndex;
+            token++;
+        }
+
+        RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer
+        parseErrorCode_ = kPointerParseErrorNone;
+        return;
+
+    error:
+        Allocator::Free(tokens_);
+        nameBuffer_ = 0;
+        tokens_ = 0;
+        tokenCount_ = 0;
+        parseErrorOffset_ = i;
+        return;
+    }
+
+    //! Stringify to string or URI fragment representation.
+    /*!
+        \tparam uriFragment True for stringifying to URI fragment representation. False for string representation.
+        \tparam OutputStream type of output stream.
+        \param os The output stream.
+    */
+    template<bool uriFragment, typename OutputStream>
+    bool Stringify(OutputStream& os) const {
+        RAPIDJSON_ASSERT(IsValid());
+
+        if (uriFragment)
+            os.Put('#');
+
+        for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+            os.Put('/');
+            for (size_t j = 0; j < t->length; j++) {
+                Ch c = t->name[j];
+                if (c == '~') {
+                    os.Put('~');
+                    os.Put('0');
+                }
+                else if (c == '/') {
+                    os.Put('~');
+                    os.Put('1');
+                }
+                else if (uriFragment && NeedPercentEncode(c)) { 
+                    // Transcode to UTF8 sequence
+                    GenericStringStream<typename ValueType::EncodingType> source(&t->name[j]);
+                    PercentEncodeStream<OutputStream> target(os);
+                    if (!Transcoder<EncodingType, UTF8<> >().Validate(source, target))
+                        return false;
+                    j += source.Tell() - 1;
+                }
+                else
+                    os.Put(c);
+            }
+        }
+        return true;
+    }
+
+    //! A helper stream for decoding a percent-encoded sequence into code unit.
+    /*!
+        This stream decodes %XY triplet into code unit (0-255).
+        If it encounters invalid characters, it sets output code unit as 0 and 
+        mark invalid, and to be checked by IsValid().
+    */
+    class PercentDecodeStream {
+    public:
+        typedef typename ValueType::Ch Ch;
+
+        //! Constructor
+        /*!
+            \param source Start of the stream
+            \param end Past-the-end of the stream.
+        */
+        PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {}
+
+        Ch Take() {
+            if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet
+                valid_ = false;
+                return 0;
+            }
+            src_++;
+            Ch c = 0;
+            for (int j = 0; j < 2; j++) {
+                c = static_cast<Ch>(c << 4);
+                Ch h = *src_;
+                if      (h >= '0' && h <= '9') c = static_cast<Ch>(c + h - '0');
+                else if (h >= 'A' && h <= 'F') c = static_cast<Ch>(c + h - 'A' + 10);
+                else if (h >= 'a' && h <= 'f') c = static_cast<Ch>(c + h - 'a' + 10);
+                else {
+                    valid_ = false;
+                    return 0;
+                }
+                src_++;
+            }
+            return c;
+        }
+
+        size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+        bool IsValid() const { return valid_; }
+
+    private:
+        const Ch* src_;     //!< Current read position.
+        const Ch* head_;    //!< Original head of the string.
+        const Ch* end_;     //!< Past-the-end position.
+        bool valid_;        //!< Whether the parsing is valid.
+    };
+
+    //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence.
+    template <typename OutputStream>
+    class PercentEncodeStream {
+    public:
+        PercentEncodeStream(OutputStream& os) : os_(os) {}
+        void Put(char c) { // UTF-8 must be byte
+            unsigned char u = static_cast<unsigned char>(c);
+            static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+            os_.Put('%');
+            os_.Put(static_cast<typename OutputStream::Ch>(hexDigits[u >> 4]));
+            os_.Put(static_cast<typename OutputStream::Ch>(hexDigits[u & 15]));
+        }
+    private:
+        OutputStream& os_;
+    };
+
+    Allocator* allocator_;                  //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
+    Allocator* ownAllocator_;               //!< Allocator owned by this Pointer.
+    Ch* nameBuffer_;                        //!< A buffer containing all names in tokens.
+    Token* tokens_;                         //!< A list of tokens.
+    size_t tokenCount_;                     //!< Number of tokens in tokens_.
+    size_t parseErrorOffset_;               //!< Offset in code unit when parsing fail.
+    PointerParseErrorCode parseErrorCode_;  //!< Parsing error code.
+};
+
+//! GenericPointer for Value (UTF-8, default allocator).
+typedef GenericPointer<Value> Pointer;
+
+//!@name Helper functions for GenericPointer
+//@{
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::AllocatorType& a) {
+    return pointer.Create(root, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Create(root, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer) {
+    return pointer.Create(document);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Create(document);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+    return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T>
+const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+    return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) {
+    return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) {
+    return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) {
+    return pointer.GetWithDefault(root, defaultValue, a);
+}
+#endif
+
+template <typename T, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 defaultValue, typename T::AllocatorType& a) {
+    return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+#endif
+
+template <typename T, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& defaultValue) {
+    return pointer.GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* defaultValue) {
+    return pointer.GetWithDefault(document, defaultValue);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& defaultValue) {
+    return pointer.GetWithDefault(document, defaultValue);
+}
+#endif
+
+template <typename DocumentType, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 defaultValue) {
+    return pointer.GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& defaultValue) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+#endif
+
+template <typename DocumentType, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) {
+    return pointer.Set(root, value, a);
+}
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) {
+    return pointer.Set(root, value, a);
+}
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* value, typename T::AllocatorType& a) {
+    return pointer.Set(root, value, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) {
+    return pointer.Set(root, value, a);
+}
+#endif
+
+template <typename T, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 value, typename T::AllocatorType& a) {
+    return pointer.Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+#endif
+
+template <typename T, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) {
+    return pointer.Set(document, value);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& value) {
+    return pointer.Set(document, value);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* value) {
+    return pointer.Set(document, value);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& value) {
+    return pointer.Set(document, value);
+}
+#endif
+
+template <typename DocumentType, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 value) {
+    return pointer.Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& value) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+#endif
+
+template <typename DocumentType, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) {
+    return pointer.Swap(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Swap(root, value, a);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) {
+    return pointer.Swap(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) {
+    return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
+    return pointer.Erase(root);
+}
+
+template <typename T, typename CharType, size_t N>
+bool EraseValueByPointer(T& root, const CharType(&source)[N]) {
+    return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root);
+}
+
+//@}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_POINTER_H_
diff --git a/src/lottie/rapidjson/prettywriter.h b/src/lottie/rapidjson/prettywriter.h
new file mode 100644 (file)
index 0000000..98dfb30
--- /dev/null
@@ -0,0 +1,277 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_PRETTYWRITER_H_
+#define RAPIDJSON_PRETTYWRITER_H_
+
+#include "writer.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Combination of PrettyWriter format flags.
+/*! \see PrettyWriter::SetFormatOptions
+ */
+enum PrettyFormatOptions {
+    kFormatDefault = 0,         //!< Default pretty formatting.
+    kFormatSingleLineArray = 1  //!< Format arrays on a single line.
+};
+
+//! Writer with indentation and spacing.
+/*!
+    \tparam OutputStream Type of ouptut os.
+    \tparam SourceEncoding Encoding of source string.
+    \tparam TargetEncoding Encoding of output stream.
+    \tparam StackAllocator Type of allocator for allocating memory of stack.
+*/
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
+public:
+    typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> Base;
+    typedef typename Base::Ch Ch;
+
+    //! Constructor
+    /*! \param os Output stream.
+        \param allocator User supplied allocator. If it is null, it will create a private one.
+        \param levelDepth Initial capacity of stack.
+    */
+    explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : 
+        Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
+
+
+    explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : 
+        Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    PrettyWriter(PrettyWriter&& rhs) :
+        Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {}
+#endif
+
+    //! Set custom indentation.
+    /*! \param indentChar       Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
+        \param indentCharCount  Number of indent characters for each indentation level.
+        \note The default indentation is 4 spaces.
+    */
+    PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
+        RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
+        indentChar_ = indentChar;
+        indentCharCount_ = indentCharCount;
+        return *this;
+    }
+
+    //! Set pretty writer formatting options.
+    /*! \param options Formatting options.
+    */
+    PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
+        formatOptions_ = options;
+        return *this;
+    }
+
+    /*! @name Implementation of Handler
+        \see Handler
+    */
+    //@{
+
+    bool Null()                 { PrettyPrefix(kNullType);   return Base::WriteNull(); }
+    bool Bool(bool b)           { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
+    bool Int(int i)             { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
+    bool Uint(unsigned u)       { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
+    bool Int64(int64_t i64)     { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
+    bool Uint64(uint64_t u64)   { PrettyPrefix(kNumberType); return Base::WriteUint64(u64);  }
+    bool Double(double d)       { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
+
+    bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+        RAPIDJSON_ASSERT(str != 0);
+        (void)copy;
+        PrettyPrefix(kNumberType);
+        return Base::WriteString(str, length);
+    }
+
+    bool String(const Ch* str, SizeType length, bool copy = false) {
+        RAPIDJSON_ASSERT(str != 0);
+        (void)copy;
+        PrettyPrefix(kStringType);
+        return Base::WriteString(str, length);
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    bool String(const std::basic_string<Ch>& str) {
+        return String(str.data(), SizeType(str.size()));
+    }
+#endif
+
+    bool StartObject() {
+        PrettyPrefix(kObjectType);
+        new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
+        return Base::WriteStartObject();
+    }
+
+    bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    bool Key(const std::basic_string<Ch>& str) {
+        return Key(str.data(), SizeType(str.size()));
+    }
+#endif
+       
+    bool EndObject(SizeType memberCount = 0) {
+        (void)memberCount;
+        RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object
+        RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object
+        RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value
+       
+        bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
+
+        if (!empty) {
+            Base::os_->Put('\n');
+            WriteIndent();
+        }
+        bool ret = Base::WriteEndObject();
+        (void)ret;
+        RAPIDJSON_ASSERT(ret == true);
+        if (Base::level_stack_.Empty()) // end of json text
+            Base::Flush();
+        return true;
+    }
+
+    bool StartArray() {
+        PrettyPrefix(kArrayType);
+        new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
+        return Base::WriteStartArray();
+    }
+
+    bool EndArray(SizeType memberCount = 0) {
+        (void)memberCount;
+        RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
+        RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
+        bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
+
+        if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
+            Base::os_->Put('\n');
+            WriteIndent();
+        }
+        bool ret = Base::WriteEndArray();
+        (void)ret;
+        RAPIDJSON_ASSERT(ret == true);
+        if (Base::level_stack_.Empty()) // end of json text
+            Base::Flush();
+        return true;
+    }
+
+    //@}
+
+    /*! @name Convenience extensions */
+    //@{
+
+    //! Simpler but slower overload.
+    bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
+    bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
+
+    //@}
+
+    //! Write a raw JSON value.
+    /*!
+        For user to write a stringified JSON as a value.
+
+        \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+        \param length Length of the json.
+        \param type Type of the root of json.
+        \note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
+    */
+    bool RawValue(const Ch* json, size_t length, Type type) {
+        RAPIDJSON_ASSERT(json != 0);
+        PrettyPrefix(type);
+        return Base::WriteRawValue(json, length);
+    }
+
+protected:
+    void PrettyPrefix(Type type) {
+        (void)type;
+        if (Base::level_stack_.GetSize() != 0) { // this value is not at root
+            typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
+
+            if (level->inArray) {
+                if (level->valueCount > 0) {
+                    Base::os_->Put(','); // add comma if it is not the first element in array
+                    if (formatOptions_ & kFormatSingleLineArray)
+                        Base::os_->Put(' ');
+                }
+
+                if (!(formatOptions_ & kFormatSingleLineArray)) {
+                    Base::os_->Put('\n');
+                    WriteIndent();
+                }
+            }
+            else {  // in object
+                if (level->valueCount > 0) {
+                    if (level->valueCount % 2 == 0) {
+                        Base::os_->Put(',');
+                        Base::os_->Put('\n');
+                    }
+                    else {
+                        Base::os_->Put(':');
+                        Base::os_->Put(' ');
+                    }
+                }
+                else
+                    Base::os_->Put('\n');
+
+                if (level->valueCount % 2 == 0)
+                    WriteIndent();
+            }
+            if (!level->inArray && level->valueCount % 2 == 0)
+                RAPIDJSON_ASSERT(type == kStringType);  // if it's in object, then even number should be a name
+            level->valueCount++;
+        }
+        else {
+            RAPIDJSON_ASSERT(!Base::hasRoot_);  // Should only has one and only one root.
+            Base::hasRoot_ = true;
+        }
+    }
+
+    void WriteIndent()  {
+        size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
+        PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count);
+    }
+
+    Ch indentChar_;
+    unsigned indentCharCount_;
+    PrettyFormatOptions formatOptions_;
+
+private:
+    // Prohibit copy constructor & assignment operator.
+    PrettyWriter(const PrettyWriter&);
+    PrettyWriter& operator=(const PrettyWriter&);
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/src/lottie/rapidjson/rapidjson.h b/src/lottie/rapidjson/rapidjson.h
new file mode 100644 (file)
index 0000000..256b0d5
--- /dev/null
@@ -0,0 +1,630 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_RAPIDJSON_H_
+#define RAPIDJSON_RAPIDJSON_H_
+
+/*!\file rapidjson.h
+    \brief common definitions and configuration
+    
+    \see RAPIDJSON_CONFIG
+ */
+
+/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
+    \brief Configuration macros for library features
+
+    Some RapidJSON features are configurable to adapt the library to a wide
+    variety of platforms, environments and usage scenarios.  Most of the
+    features can be configured in terms of overriden or predefined
+    preprocessor macros at compile-time.
+
+    Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
+
+    \note These macros should be given on the compiler command-line
+          (where applicable)  to avoid inconsistent values when compiling
+          different translation units of a single application.
+ */
+
+#include <cstdlib>  // malloc(), realloc(), free(), size_t
+#include <cstring>  // memset(), memcpy(), memmove(), memcmp()
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_VERSION_STRING
+//
+// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt.
+//
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+// token stringification
+#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
+#define RAPIDJSON_DO_STRINGIFY(x) #x
+
+// token concatenation
+#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
+#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
+#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
+//!@endcond
+
+/*! \def RAPIDJSON_MAJOR_VERSION
+    \ingroup RAPIDJSON_CONFIG
+    \brief Major version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_MINOR_VERSION
+    \ingroup RAPIDJSON_CONFIG
+    \brief Minor version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_PATCH_VERSION
+    \ingroup RAPIDJSON_CONFIG
+    \brief Patch version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_VERSION_STRING
+    \ingroup RAPIDJSON_CONFIG
+    \brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
+*/
+#define RAPIDJSON_MAJOR_VERSION 1
+#define RAPIDJSON_MINOR_VERSION 1
+#define RAPIDJSON_PATCH_VERSION 0
+#define RAPIDJSON_VERSION_STRING \
+    RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION)
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NAMESPACE_(BEGIN|END)
+/*! \def RAPIDJSON_NAMESPACE
+    \ingroup RAPIDJSON_CONFIG
+    \brief   provide custom rapidjson namespace
+
+    In order to avoid symbol clashes and/or "One Definition Rule" errors
+    between multiple inclusions of (different versions of) RapidJSON in
+    a single binary, users can customize the name of the main RapidJSON
+    namespace.
+
+    In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
+    to a custom name (e.g. \c MyRapidJSON) is sufficient.  If multiple
+    levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
+    RAPIDJSON_NAMESPACE_END need to be defined as well:
+
+    \code
+    // in some .cpp file
+    #define RAPIDJSON_NAMESPACE my::rapidjson
+    #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
+    #define RAPIDJSON_NAMESPACE_END   } }
+    #include "rapidjson/..."
+    \endcode
+
+    \see rapidjson
+ */
+/*! \def RAPIDJSON_NAMESPACE_BEGIN
+    \ingroup RAPIDJSON_CONFIG
+    \brief   provide custom rapidjson namespace (opening expression)
+    \see RAPIDJSON_NAMESPACE
+*/
+/*! \def RAPIDJSON_NAMESPACE_END
+    \ingroup RAPIDJSON_CONFIG
+    \brief   provide custom rapidjson namespace (closing expression)
+    \see RAPIDJSON_NAMESPACE
+*/
+#ifndef RAPIDJSON_NAMESPACE
+#define RAPIDJSON_NAMESPACE rapidjson
+#endif
+#ifndef RAPIDJSON_NAMESPACE_BEGIN
+#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
+#endif
+#ifndef RAPIDJSON_NAMESPACE_END
+#define RAPIDJSON_NAMESPACE_END }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_HAS_STDSTRING
+
+#ifndef RAPIDJSON_HAS_STDSTRING
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
+#else
+#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
+#endif
+/*! \def RAPIDJSON_HAS_STDSTRING
+    \ingroup RAPIDJSON_CONFIG
+    \brief Enable RapidJSON support for \c std::string
+
+    By defining this preprocessor symbol to \c 1, several convenience functions for using
+    \ref rapidjson::GenericValue with \c std::string are enabled, especially
+    for construction and comparison.
+
+    \hideinitializer
+*/
+#endif // !defined(RAPIDJSON_HAS_STDSTRING)
+
+#if RAPIDJSON_HAS_STDSTRING
+#include <string>
+#endif // RAPIDJSON_HAS_STDSTRING
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NO_INT64DEFINE
+
+/*! \def RAPIDJSON_NO_INT64DEFINE
+    \ingroup RAPIDJSON_CONFIG
+    \brief Use external 64-bit integer types.
+
+    RapidJSON requires the 64-bit integer types \c int64_t and  \c uint64_t types
+    to be available at global scope.
+
+    If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
+    prevent RapidJSON from defining its own types.
+*/
+#ifndef RAPIDJSON_NO_INT64DEFINE
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#if defined(_MSC_VER) && (_MSC_VER < 1800)     // Visual Studio 2013
+#include "msinttypes/stdint.h"
+#include "msinttypes/inttypes.h"
+#else
+// Other compilers should have this.
+#include <stdint.h>
+#include <inttypes.h>
+#endif
+//!@endcond
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_NO_INT64DEFINE
+#endif
+#endif // RAPIDJSON_NO_INT64TYPEDEF
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_FORCEINLINE
+
+#ifndef RAPIDJSON_FORCEINLINE
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#if defined(_MSC_VER) && defined(NDEBUG)
+#define RAPIDJSON_FORCEINLINE __forceinline
+#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
+#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
+#else
+#define RAPIDJSON_FORCEINLINE
+#endif
+//!@endcond
+#endif // RAPIDJSON_FORCEINLINE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ENDIAN
+#define RAPIDJSON_LITTLEENDIAN  0   //!< Little endian machine
+#define RAPIDJSON_BIGENDIAN     1   //!< Big endian machine
+
+//! Endianness of the machine.
+/*!
+    \def RAPIDJSON_ENDIAN
+    \ingroup RAPIDJSON_CONFIG
+
+    GCC 4.6 provided macro for detecting endianness of the target machine. But other
+    compilers may not have this. User can define RAPIDJSON_ENDIAN to either
+    \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
+
+    Default detection implemented with reference to
+    \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
+    \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
+*/
+#ifndef RAPIDJSON_ENDIAN
+// Detect with GCC 4.6's macro
+#  ifdef __BYTE_ORDER__
+#    if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#      define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+#    elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#      define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+#    else
+#      error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+#    endif // __BYTE_ORDER__
+// Detect with GLIBC's endian.h
+#  elif defined(__GLIBC__)
+#    include <endian.h>
+#    if (__BYTE_ORDER == __LITTLE_ENDIAN)
+#      define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+#    elif (__BYTE_ORDER == __BIG_ENDIAN)
+#      define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+#    else
+#      error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+#   endif // __GLIBC__
+// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
+#  elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+#    define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+#  elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+#    define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+// Detect with architecture macros
+#  elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
+#    define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+#  elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
+#    define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+#  elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+#    define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+#  elif defined(RAPIDJSON_DOXYGEN_RUNNING)
+#    define RAPIDJSON_ENDIAN
+#  else
+#    error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.   
+#  endif
+#endif // RAPIDJSON_ENDIAN
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_64BIT
+
+//! Whether using 64-bit architecture
+#ifndef RAPIDJSON_64BIT
+#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__)
+#define RAPIDJSON_64BIT 1
+#else
+#define RAPIDJSON_64BIT 0
+#endif
+#endif // RAPIDJSON_64BIT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ALIGN
+
+//! Data alignment of the machine.
+/*! \ingroup RAPIDJSON_CONFIG
+    \param x pointer to align
+
+    Some machines require strict data alignment. Currently the default uses 4 bytes
+    alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
+    User can customize by defining the RAPIDJSON_ALIGN function macro.
+*/
+#ifndef RAPIDJSON_ALIGN
+#if RAPIDJSON_64BIT == 1
+#define RAPIDJSON_ALIGN(x) (((x) + static_cast<uint64_t>(7u)) & ~static_cast<uint64_t>(7u))
+#else
+#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_UINT64_C2
+
+//! Construct a 64-bit literal by a pair of 32-bit integer.
+/*!
+    64-bit literal with or without ULL suffix is prone to compiler warnings.
+    UINT64_C() is C macro which cause compilation problems.
+    Use this macro to define 64-bit constants by a pair of 32-bit integer.
+*/
+#ifndef RAPIDJSON_UINT64_C2
+#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+//! Use only lower 48-bit address for some pointers.
+/*!
+    \ingroup RAPIDJSON_CONFIG
+
+    This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
+    The higher 16-bit can be used for storing other data.
+    \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
+*/
+#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
+#else
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
+#endif
+#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
+#if RAPIDJSON_64BIT != 1
+#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
+#endif
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
+#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
+#else
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
+#define RAPIDJSON_GETPOINTER(type, p) (p)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD
+
+/*! \def RAPIDJSON_SIMD
+    \ingroup RAPIDJSON_CONFIG
+    \brief Enable SSE2/SSE4.2/Neon optimization.
+
+    RapidJSON supports optimized implementations for some parsing operations
+    based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel
+    or ARM compatible processors.
+
+    To enable these optimizations, three different symbols can be defined;
+    \code
+    // Enable SSE2 optimization.
+    #define RAPIDJSON_SSE2
+
+    // Enable SSE4.2 optimization.
+    #define RAPIDJSON_SSE42
+    \endcode
+
+    // Enable ARM Neon optimization.
+    #define RAPIDJSON_NEON
+    \endcode
+
+    \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined.
+
+    If any of these symbols is defined, RapidJSON defines the macro
+    \c RAPIDJSON_SIMD to indicate the availability of the optimized code.
+*/
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
+    || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING)
+#define RAPIDJSON_SIMD
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NO_SIZETYPEDEFINE
+
+#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
+/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
+    \ingroup RAPIDJSON_CONFIG
+    \brief User-provided \c SizeType definition.
+
+    In order to avoid using 32-bit size types for indexing strings and arrays,
+    define this preprocessor symbol and provide the type rapidjson::SizeType
+    before including RapidJSON:
+    \code
+    #define RAPIDJSON_NO_SIZETYPEDEFINE
+    namespace rapidjson { typedef ::std::size_t SizeType; }
+    #include "rapidjson/..."
+    \endcode
+
+    \see rapidjson::SizeType
+*/
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_NO_SIZETYPEDEFINE
+#endif
+RAPIDJSON_NAMESPACE_BEGIN
+//! Size type (for string lengths, array sizes, etc.)
+/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
+    instead of using \c size_t. Users may override the SizeType by defining
+    \ref RAPIDJSON_NO_SIZETYPEDEFINE.
+*/
+typedef unsigned SizeType;
+RAPIDJSON_NAMESPACE_END
+#endif
+
+// always import std::size_t to rapidjson namespace
+RAPIDJSON_NAMESPACE_BEGIN
+using std::size_t;
+RAPIDJSON_NAMESPACE_END
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ASSERT
+
+//! Assertion.
+/*! \ingroup RAPIDJSON_CONFIG
+    By default, rapidjson uses C \c assert() for internal assertions.
+    User can override it by defining RAPIDJSON_ASSERT(x) macro.
+
+    \note Parsing errors are handled and can be customized by the
+          \ref RAPIDJSON_ERRORS APIs.
+*/
+#ifndef RAPIDJSON_ASSERT
+#include <cassert>
+#define RAPIDJSON_ASSERT(x) assert(x)
+#endif // RAPIDJSON_ASSERT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_STATIC_ASSERT
+
+// Prefer C++11 static_assert, if available
+#ifndef RAPIDJSON_STATIC_ASSERT
+#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
+#define RAPIDJSON_STATIC_ASSERT(x) \
+   static_assert(x, RAPIDJSON_STRINGIFY(x))
+#endif // C++11
+#endif // RAPIDJSON_STATIC_ASSERT
+
+// Adopt C++03 implementation from boost
+#ifndef RAPIDJSON_STATIC_ASSERT
+#ifndef __clang__
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#endif
+RAPIDJSON_NAMESPACE_BEGIN
+template <bool x> struct STATIC_ASSERTION_FAILURE;
+template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
+template <size_t x> struct StaticAssertTest {};
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__GNUC__)
+#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
+#else
+#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE 
+#endif
+#ifndef __clang__
+//!@endcond
+#endif
+
+/*! \def RAPIDJSON_STATIC_ASSERT
+    \brief (Internal) macro to check for conditions at compile-time
+    \param x compile-time condition
+    \hideinitializer
+ */
+#define RAPIDJSON_STATIC_ASSERT(x) \
+    typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
+      sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
+    RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
+#endif // RAPIDJSON_STATIC_ASSERT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
+
+//! Compiler branching hint for expression with high probability to be true.
+/*!
+    \ingroup RAPIDJSON_CONFIG
+    \param x Boolean expression likely to be true.
+*/
+#ifndef RAPIDJSON_LIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define RAPIDJSON_LIKELY(x) (x)
+#endif
+#endif
+
+//! Compiler branching hint for expression with low probability to be true.
+/*!
+    \ingroup RAPIDJSON_CONFIG
+    \param x Boolean expression unlikely to be true.
+*/
+#ifndef RAPIDJSON_UNLIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define RAPIDJSON_UNLIKELY(x) (x)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+
+#define RAPIDJSON_MULTILINEMACRO_BEGIN do {  
+#define RAPIDJSON_MULTILINEMACRO_END \
+} while((void)0, 0)
+
+// adopted from Boost
+#define RAPIDJSON_VERSION_CODE(x,y,z) \
+  (((x)*100000) + ((y)*100) + (z))
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
+
+#if defined(__GNUC__)
+#define RAPIDJSON_GNUC \
+    RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
+#endif
+
+#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
+
+#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
+#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
+#define RAPIDJSON_DIAG_OFF(x) \
+    RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
+
+// push/pop support in Clang and GCC>=4.6
+#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
+#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
+#define RAPIDJSON_DIAG_POP  RAPIDJSON_DIAG_PRAGMA(pop)
+#else // GCC >= 4.2, < 4.6
+#define RAPIDJSON_DIAG_PUSH /* ignored */
+#define RAPIDJSON_DIAG_POP /* ignored */
+#endif
+
+#elif defined(_MSC_VER)
+
+// pragma (MSVC specific)
+#define RAPIDJSON_PRAGMA(x) __pragma(x)
+#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
+
+#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
+#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
+#define RAPIDJSON_DIAG_POP  RAPIDJSON_DIAG_PRAGMA(pop)
+
+#else
+
+#define RAPIDJSON_DIAG_OFF(x) /* ignored */
+#define RAPIDJSON_DIAG_PUSH   /* ignored */
+#define RAPIDJSON_DIAG_POP    /* ignored */
+
+#endif // RAPIDJSON_DIAG_*
+
+///////////////////////////////////////////////////////////////////////////////
+// C++11 features
+//enable c++11 feature
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+
+#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#if defined(__clang__)
+#if __has_feature(cxx_rvalue_references) && \
+    (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#else
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
+#endif
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+      (defined(_MSC_VER) && _MSC_VER >= 1600)
+
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#else
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
+//    (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
+#else
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
+#endif
+#endif
+#if RAPIDJSON_HAS_CXX11_NOEXCEPT
+#define RAPIDJSON_NOEXCEPT noexcept
+#else
+#define RAPIDJSON_NOEXCEPT /* noexcept */
+#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
+
+// no automatic detection, yet
+#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
+#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
+#endif
+
+#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+      (defined(_MSC_VER) && _MSC_VER >= 1700)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
+#else
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
+
+//!@endcond
+
+///////////////////////////////////////////////////////////////////////////////
+// new/delete
+
+#ifndef RAPIDJSON_NEW
+///! customization point for global \c new
+#define RAPIDJSON_NEW(TypeName) new TypeName
+#endif
+#ifndef RAPIDJSON_DELETE
+///! customization point for global \c delete
+#define RAPIDJSON_DELETE(x) delete x
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Type
+
+/*! \namespace rapidjson
+    \brief main RapidJSON namespace
+    \see RAPIDJSON_NAMESPACE
+*/
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Type of JSON value
+enum Type {
+    kNullType = 0,      //!< null
+    kFalseType = 1,     //!< false
+    kTrueType = 2,      //!< true
+    kObjectType = 3,    //!< object
+    kArrayType = 4,     //!< array 
+    kStringType = 5,    //!< string
+    kNumberType = 6     //!< number
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/src/lottie/rapidjson/reader.h b/src/lottie/rapidjson/reader.h
new file mode 100644 (file)
index 0000000..120c311
--- /dev/null
@@ -0,0 +1,2221 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_READER_H_
+#define RAPIDJSON_READER_H_
+
+/*! \file reader.h */
+
+#include "allocators.h"
+#include "stream.h"
+#include "encodedstream.h"
+#include "internal/meta.h"
+#include "internal/stack.h"
+#include "internal/strtod.h"
+#include <limits>
+
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#elif defined(RAPIDJSON_NEON)
+#include <arm_neon.h>
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4127)  // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4702)  // unreachable code
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(old-style-cast)
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define RAPIDJSON_NOTHING /* deliberately empty */
+#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN
+#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \
+    RAPIDJSON_MULTILINEMACRO_BEGIN \
+    if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \
+    RAPIDJSON_MULTILINEMACRO_END
+#endif
+#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \
+    RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING)
+//!@endcond
+
+/*! \def RAPIDJSON_PARSE_ERROR_NORETURN
+    \ingroup RAPIDJSON_ERRORS
+    \brief Macro to indicate a parse error.
+    \param parseErrorCode \ref rapidjson::ParseErrorCode of the error
+    \param offset  position of the error in JSON input (\c size_t)
+
+    This macros can be used as a customization point for the internal
+    error handling mechanism of RapidJSON.
+
+    A common usage model is to throw an exception instead of requiring the
+    caller to explicitly check the \ref rapidjson::GenericReader::Parse's
+    return value:
+
+    \code
+    #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \
+       throw ParseException(parseErrorCode, #parseErrorCode, offset)
+
+    #include <stdexcept>               // std::runtime_error
+    #include "rapidjson/error/error.h" // rapidjson::ParseResult
+
+    struct ParseException : std::runtime_error, rapidjson::ParseResult {
+      ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset)
+        : std::runtime_error(msg), ParseResult(code, offset) {}
+    };
+
+    #include "rapidjson/reader.h"
+    \endcode
+
+    \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse
+ */
+#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
+#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
+    RAPIDJSON_MULTILINEMACRO_BEGIN \
+    RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \
+    SetParseError(parseErrorCode, offset); \
+    RAPIDJSON_MULTILINEMACRO_END
+#endif
+
+/*! \def RAPIDJSON_PARSE_ERROR
+    \ingroup RAPIDJSON_ERRORS
+    \brief (Internal) macro to indicate and handle a parse error.
+    \param parseErrorCode \ref rapidjson::ParseErrorCode of the error
+    \param offset  position of the error in JSON input (\c size_t)
+
+    Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing.
+
+    \see RAPIDJSON_PARSE_ERROR_NORETURN
+    \hideinitializer
+ */
+#ifndef RAPIDJSON_PARSE_ERROR
+#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
+    RAPIDJSON_MULTILINEMACRO_BEGIN \
+    RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
+    RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \
+    RAPIDJSON_MULTILINEMACRO_END
+#endif
+
+#include "error/error.h" // ParseErrorCode, ParseResult
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// ParseFlag
+
+/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS
+    \ingroup RAPIDJSON_CONFIG
+    \brief User-defined kParseDefaultFlags definition.
+
+    User can define this as any \c ParseFlag combinations.
+*/
+#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS
+#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags
+#endif
+
+//! Combination of parseFlags
+/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream
+ */
+enum ParseFlag {
+    kParseNoFlags = 0,              //!< No flags are set.
+    kParseInsituFlag = 1,           //!< In-situ(destructive) parsing.
+    kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings.
+    kParseIterativeFlag = 4,        //!< Iterative(constant complexity in terms of function call stack size) parsing.
+    kParseStopWhenDoneFlag = 8,     //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
+    kParseFullPrecisionFlag = 16,   //!< Parse number in full precision (but slower).
+    kParseCommentsFlag = 32,        //!< Allow one-line (//) and multi-line (/**/) comments.
+    kParseNumbersAsStringsFlag = 64,    //!< Parse all numbers (ints/doubles) as strings.
+    kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
+    kParseNanAndInfFlag = 256,      //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
+    kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS  //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Handler
+
+/*! \class rapidjson::Handler
+    \brief Concept for receiving events from GenericReader upon parsing.
+    The functions return true if no error occurs. If they return false,
+    the event publisher should terminate the process.
+\code
+concept Handler {
+    typename Ch;
+
+    bool Null();
+    bool Bool(bool b);
+    bool Int(int i);
+    bool Uint(unsigned i);
+    bool Int64(int64_t i);
+    bool Uint64(uint64_t i);
+    bool Double(double d);
+    /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+    bool RawNumber(const Ch* str, SizeType length, bool copy);
+    bool String(const Ch* str, SizeType length, bool copy);
+    bool StartObject();
+    bool Key(const Ch* str, SizeType length, bool copy);
+    bool EndObject(SizeType memberCount);
+    bool StartArray();
+    bool EndArray(SizeType elementCount);
+};
+\endcode
+*/
+///////////////////////////////////////////////////////////////////////////////
+// BaseReaderHandler
+
+//! Default implementation of Handler.
+/*! This can be used as base class of any reader handler.
+    \note implements Handler concept
+*/
+template<typename Encoding = UTF8<>, typename Derived = void>
+struct BaseReaderHandler {
+    typedef typename Encoding::Ch Ch;
+
+    typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override;
+
+    bool Default() { return true; }
+    bool Null() { return static_cast<Override&>(*this).Default(); }
+    bool Bool(bool) { return static_cast<Override&>(*this).Default(); }
+    bool Int(int) { return static_cast<Override&>(*this).Default(); }
+    bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); }
+    bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
+    bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
+    bool Double(double) { return static_cast<Override&>(*this).Default(); }
+    /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+    bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
+    bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
+    bool StartObject() { return static_cast<Override&>(*this).Default(); }
+    bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
+    bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); }
+    bool StartArray() { return static_cast<Override&>(*this).Default(); }
+    bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamLocalCopy
+
+namespace internal {
+
+template<typename Stream, int = StreamTraits<Stream>::copyOptimization>
+class StreamLocalCopy;
+
+//! Do copy optimization.
+template<typename Stream>
+class StreamLocalCopy<Stream, 1> {
+public:
+    StreamLocalCopy(Stream& original) : s(original), original_(original) {}
+    ~StreamLocalCopy() { original_ = s; }
+
+    Stream s;
+
+private:
+    StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */;
+
+    Stream& original_;
+};
+
+//! Keep reference.
+template<typename Stream>
+class StreamLocalCopy<Stream, 0> {
+public:
+    StreamLocalCopy(Stream& original) : s(original) {}
+
+    Stream& s;
+
+private:
+    StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */;
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// SkipWhitespace
+
+//! Skip the JSON white spaces in a stream.
+/*! \param is A input stream for skipping white spaces.
+    \note This function has SSE2/SSE4.2 specialization.
+*/
+template<typename InputStream>
+void SkipWhitespace(InputStream& is) {
+    internal::StreamLocalCopy<InputStream> copy(is);
+    InputStream& s(copy.s);
+
+    typename InputStream::Ch c;
+    while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t')
+        s.Take();
+}
+
+inline const char* SkipWhitespace(const char* p, const char* end) {
+    while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+        ++p;
+    return p;
+}
+
+#ifdef RAPIDJSON_SSE42
+//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+    // Fast return for single non-whitespace
+    if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+        ++p;
+    else
+        return p;
+
+    // 16-byte align to the next boundary
+    const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+    while (p != nextAligned)
+        if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+            ++p;
+        else
+            return p;
+
+    // The rest of string using SIMD
+    static const char whitespace[16] = " \n\r\t";
+    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+    for (;; p += 16) {
+        const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+        const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY);
+        if (r != 16)    // some of characters is non-whitespace
+            return p + r;
+    }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+    // Fast return for single non-whitespace
+    if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+        ++p;
+    else
+        return p;
+
+    // The middle of string using SIMD
+    static const char whitespace[16] = " \n\r\t";
+    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+    for (; p <= end - 16; p += 16) {
+        const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+        const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY);
+        if (r != 16)    // some of characters is non-whitespace
+            return p + r;
+    }
+
+    return SkipWhitespace(p, end);
+}
+
+#elif defined(RAPIDJSON_SSE2)
+
+//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+    // Fast return for single non-whitespace
+    if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+        ++p;
+    else
+        return p;
+
+    // 16-byte align to the next boundary
+    const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+    while (p != nextAligned)
+        if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+            ++p;
+        else
+            return p;
+
+    // The rest of string
+    #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+    static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+    #undef C16
+
+    const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+    const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+    const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+    const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+    for (;; p += 16) {
+        const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+        __m128i x = _mm_cmpeq_epi8(s, w0);
+        x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+        x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+        x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+        unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+        if (r != 0) {   // some of characters may be non-whitespace
+#ifdef _MSC_VER         // Find the index of first non-whitespace
+            unsigned long offset;
+            _BitScanForward(&offset, r);
+            return p + offset;
+#else
+            return p + __builtin_ffs(r) - 1;
+#endif
+        }
+    }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+    // Fast return for single non-whitespace
+    if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+        ++p;
+    else
+        return p;
+
+    // The rest of string
+    #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+    static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+    #undef C16
+
+    const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+    const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+    const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+    const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+    for (; p <= end - 16; p += 16) {
+        const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+        __m128i x = _mm_cmpeq_epi8(s, w0);
+        x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+        x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+        x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+        unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+        if (r != 0) {   // some of characters may be non-whitespace
+#ifdef _MSC_VER         // Find the index of first non-whitespace
+            unsigned long offset;
+            _BitScanForward(&offset, r);
+            return p + offset;
+#else
+            return p + __builtin_ffs(r) - 1;
+#endif
+        }
+    }
+
+    return SkipWhitespace(p, end);
+}
+
+#elif defined(RAPIDJSON_NEON)
+
+//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+    // Fast return for single non-whitespace
+    if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+        ++p;
+    else
+        return p;
+
+    // 16-byte align to the next boundary
+    const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+    while (p != nextAligned)
+        if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+            ++p;
+        else
+            return p;
+
+    const uint8x16_t w0 = vmovq_n_u8(' ');
+    const uint8x16_t w1 = vmovq_n_u8('\n');
+    const uint8x16_t w2 = vmovq_n_u8('\r');
+    const uint8x16_t w3 = vmovq_n_u8('\t');
+
+    for (;; p += 16) {
+        const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p));
+        uint8x16_t x = vceqq_u8(s, w0);
+        x = vorrq_u8(x, vceqq_u8(s, w1));
+        x = vorrq_u8(x, vceqq_u8(s, w2));
+        x = vorrq_u8(x, vceqq_u8(s, w3));
+
+        x = vmvnq_u8(x);                       // Negate
+        x = vrev64q_u8(x);                     // Rev in 64
+        uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
+        uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+
+        if (low == 0) {
+            if (high != 0) {
+                int lz =__builtin_clzll(high);;
+                return p + 8 + (lz >> 3);
+            }
+        } else {
+            int lz = __builtin_clzll(low);;
+            return p + (lz >> 3);
+        }
+    }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+    // Fast return for single non-whitespace
+    if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+        ++p;
+    else
+        return p;
+
+    const uint8x16_t w0 = vmovq_n_u8(' ');
+    const uint8x16_t w1 = vmovq_n_u8('\n');
+    const uint8x16_t w2 = vmovq_n_u8('\r');
+    const uint8x16_t w3 = vmovq_n_u8('\t');
+
+    for (; p <= end - 16; p += 16) {
+        const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p));
+        uint8x16_t x = vceqq_u8(s, w0);
+        x = vorrq_u8(x, vceqq_u8(s, w1));
+        x = vorrq_u8(x, vceqq_u8(s, w2));
+        x = vorrq_u8(x, vceqq_u8(s, w3));
+
+        x = vmvnq_u8(x);                       // Negate
+        x = vrev64q_u8(x);                     // Rev in 64
+        uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
+        uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+
+        if (low == 0) {
+            if (high != 0) {
+                int lz = __builtin_clzll(high);
+                return p + 8 + (lz >> 3);
+            }
+        } else {
+            int lz = __builtin_clzll(low);
+            return p + (lz >> 3);
+        }
+    }
+
+    return SkipWhitespace(p, end);
+}
+
+#endif // RAPIDJSON_NEON
+
+#ifdef RAPIDJSON_SIMD
+//! Template function specialization for InsituStringStream
+template<> inline void SkipWhitespace(InsituStringStream& is) {
+    is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_));
+}
+
+//! Template function specialization for StringStream
+template<> inline void SkipWhitespace(StringStream& is) {
+    is.src_ = SkipWhitespace_SIMD(is.src_);
+}
+
+template<> inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) {
+    is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_);
+}
+#endif // RAPIDJSON_SIMD
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericReader
+
+//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator.
+/*! GenericReader parses JSON text from a stream, and send events synchronously to an
+    object implementing Handler concept.
+
+    It needs to allocate a stack for storing a single decoded string during
+    non-destructive parsing.
+
+    For in-situ parsing, the decoded string is directly written to the source
+    text string, no temporary buffer is required.
+
+    A GenericReader object can be reused for parsing multiple JSON text.
+
+    \tparam SourceEncoding Encoding of the input stream.
+    \tparam TargetEncoding Encoding of the parse output.
+    \tparam StackAllocator Allocator type for stack.
+*/
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator = CrtAllocator>
+class GenericReader {
+public:
+    typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type
+
+    //! Constructor.
+    /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
+        \param stackCapacity stack capacity in bytes for storing a single decoded string.  (Only use for non-destructive parsing)
+    */
+    GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {}
+
+    //! Parse JSON text.
+    /*! \tparam parseFlags Combination of \ref ParseFlag.
+        \tparam InputStream Type of input stream, implementing Stream concept.
+        \tparam Handler Type of handler, implementing Handler concept.
+        \param is Input stream to be parsed.
+        \param handler The handler to receive events.
+        \return Whether the parsing is successful.
+    */
+    template <unsigned parseFlags, typename InputStream, typename Handler>
+    ParseResult Parse(InputStream& is, Handler& handler) {
+        if (parseFlags & kParseIterativeFlag)
+            return IterativeParse<parseFlags>(is, handler);
+
+        parseResult_.Clear();
+
+        ClearStackOnExit scope(*this);
+
+        SkipWhitespaceAndComments<parseFlags>(is);
+        RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+        if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) {
+            RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+        }
+        else {
+            ParseValue<parseFlags>(is, handler);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+            if (!(parseFlags & kParseStopWhenDoneFlag)) {
+                SkipWhitespaceAndComments<parseFlags>(is);
+                RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+                if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) {
+                    RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
+                    RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+                }
+            }
+        }
+
+        return parseResult_;
+    }
+
+    //! Parse JSON text (with \ref kParseDefaultFlags)
+    /*! \tparam InputStream Type of input stream, implementing Stream concept
+        \tparam Handler Type of handler, implementing Handler concept.
+        \param is Input stream to be parsed.
+        \param handler The handler to receive events.
+        \return Whether the parsing is successful.
+    */
+    template <typename InputStream, typename Handler>
+    ParseResult Parse(InputStream& is, Handler& handler) {
+        return Parse<kParseDefaultFlags>(is, handler);
+    }
+
+    //! Initialize JSON text token-by-token parsing
+    /*!
+     */
+    void IterativeParseInit() {
+        parseResult_.Clear();
+        state_ = IterativeParsingStartState;
+    }
+    
+    //! Parse one token from JSON text
+    /*! \tparam InputStream Type of input stream, implementing Stream concept
+        \tparam Handler Type of handler, implementing Handler concept.
+        \param is Input stream to be parsed.
+        \param handler The handler to receive events.
+        \return Whether the parsing is successful.
+     */
+    template <unsigned parseFlags, typename InputStream, typename Handler>
+    bool IterativeParseNext(InputStream& is, Handler& handler) {
+        while (RAPIDJSON_LIKELY(is.Peek() != '\0')) {
+            SkipWhitespaceAndComments<parseFlags>(is);
+            
+            Token t = Tokenize(is.Peek());
+            IterativeParsingState n = Predict(state_, t);
+            IterativeParsingState d = Transit<parseFlags>(state_, t, n, is, handler);
+            
+            // If we've finished or hit an error...
+            if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) {
+                // Report errors.
+                if (d == IterativeParsingErrorState) {
+                    HandleError(state_, is);
+                    return false;
+                }
+            
+                // Transition to the finish state.
+                RAPIDJSON_ASSERT(d == IterativeParsingFinishState);
+                state_ = d;
+                
+                // If StopWhenDone is not set...
+                if (!(parseFlags & kParseStopWhenDoneFlag)) {
+                    // ... and extra non-whitespace data is found...
+                    SkipWhitespaceAndComments<parseFlags>(is);
+                    if (is.Peek() != '\0') {
+                        // ... this is considered an error.
+                        HandleError(state_, is);
+                        return false;
+                    }
+                }
+                
+                // Success! We are done!
+                return true;
+            }
+            
+            // Transition to the new state.
+            state_ = d;
+
+            // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now.
+            if (!IsIterativeParsingDelimiterState(n))
+                return true;
+        }
+        
+        // We reached the end of file.
+        stack_.Clear();
+
+        if (state_ != IterativeParsingFinishState) {
+            HandleError(state_, is);
+            return false;
+        }
+        
+        return true;
+    }
+    
+    //! Check if token-by-token parsing JSON text is complete
+    /*! \return Whether the JSON has been fully decoded.
+     */
+    RAPIDJSON_FORCEINLINE bool IterativeParseComplete() {
+        return IsIterativeParsingCompleteState(state_);
+    }
+
+    //! Whether a parse error has occured in the last parsing.
+    bool HasParseError() const { return parseResult_.IsError(); }
+
+    //! Get the \ref ParseErrorCode of last parsing.
+    ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); }
+
+    //! Get the position of last parsing error in input, 0 otherwise.
+    size_t GetErrorOffset() const { return parseResult_.Offset(); }
+
+protected:
+    void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); }
+
+private:
+    // Prohibit copy constructor & assignment operator.
+    GenericReader(const GenericReader&);
+    GenericReader& operator=(const GenericReader&);
+
+    void ClearStack() { stack_.Clear(); }
+
+    // clear stack on any exit from ParseStream, e.g. due to exception
+    struct ClearStackOnExit {
+        explicit ClearStackOnExit(GenericReader& r) : r_(r) {}
+        ~ClearStackOnExit() { r_.ClearStack(); }
+    private:
+        GenericReader& r_;
+        ClearStackOnExit(const ClearStackOnExit&);
+        ClearStackOnExit& operator=(const ClearStackOnExit&);
+    };
+
+    template<unsigned parseFlags, typename InputStream>
+    void SkipWhitespaceAndComments(InputStream& is) {
+        SkipWhitespace(is);
+
+        if (parseFlags & kParseCommentsFlag) {
+            while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) {
+                if (Consume(is, '*')) {
+                    while (true) {
+                        if (RAPIDJSON_UNLIKELY(is.Peek() == '\0'))
+                            RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
+                        else if (Consume(is, '*')) {
+                            if (Consume(is, '/'))
+                                break;
+                        }
+                        else
+                            is.Take();
+                    }
+                }
+                else if (RAPIDJSON_LIKELY(Consume(is, '/')))
+                    while (is.Peek() != '\0' && is.Take() != '\n') {}
+                else
+                    RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
+
+                SkipWhitespace(is);
+            }
+        }
+    }
+
+    // Parse object: { string : value, ... }
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseObject(InputStream& is, Handler& handler) {
+        RAPIDJSON_ASSERT(is.Peek() == '{');
+        is.Take();  // Skip '{'
+
+        if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
+            RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+
+        SkipWhitespaceAndComments<parseFlags>(is);
+        RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+        if (Consume(is, '}')) {
+            if (RAPIDJSON_UNLIKELY(!handler.EndObject(0)))  // empty object
+                RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+            return;
+        }
+
+        for (SizeType memberCount = 0;;) {
+            if (RAPIDJSON_UNLIKELY(is.Peek() != '"'))
+                RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
+
+            ParseString<parseFlags>(is, handler, true);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            SkipWhitespaceAndComments<parseFlags>(is);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            if (RAPIDJSON_UNLIKELY(!Consume(is, ':')))
+                RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
+
+            SkipWhitespaceAndComments<parseFlags>(is);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            ParseValue<parseFlags>(is, handler);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            SkipWhitespaceAndComments<parseFlags>(is);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            ++memberCount;
+
+            switch (is.Peek()) {
+                case ',':
+                    is.Take();
+                    SkipWhitespaceAndComments<parseFlags>(is);
+                    RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+                    break;
+                case '}':
+                    is.Take();
+                    if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+                        RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+                    return;
+                default:
+                    RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy
+            }
+
+            if (parseFlags & kParseTrailingCommasFlag) {
+                if (is.Peek() == '}') {
+                    if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+                        RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+                    is.Take();
+                    return;
+                }
+            }
+        }
+    }
+
+    // Parse array: [ value, ... ]
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseArray(InputStream& is, Handler& handler) {
+        RAPIDJSON_ASSERT(is.Peek() == '[');
+        is.Take();  // Skip '['
+
+        if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
+            RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+
+        SkipWhitespaceAndComments<parseFlags>(is);
+        RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+        if (Consume(is, ']')) {
+            if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array
+                RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+            return;
+        }
+
+        for (SizeType elementCount = 0;;) {
+            ParseValue<parseFlags>(is, handler);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            ++elementCount;
+            SkipWhitespaceAndComments<parseFlags>(is);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+            if (Consume(is, ',')) {
+                SkipWhitespaceAndComments<parseFlags>(is);
+                RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+            }
+            else if (Consume(is, ']')) {
+                if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+                    RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+                return;
+            }
+            else
+                RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
+
+            if (parseFlags & kParseTrailingCommasFlag) {
+                if (is.Peek() == ']') {
+                    if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+                        RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+                    is.Take();
+                    return;
+                }
+            }
+        }
+    }
+
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseNull(InputStream& is, Handler& handler) {
+        RAPIDJSON_ASSERT(is.Peek() == 'n');
+        is.Take();
+
+        if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) {
+            if (RAPIDJSON_UNLIKELY(!handler.Null()))
+                RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+        }
+        else
+            RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+    }
+
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseTrue(InputStream& is, Handler& handler) {
+        RAPIDJSON_ASSERT(is.Peek() == 't');
+        is.Take();
+
+        if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) {
+            if (RAPIDJSON_UNLIKELY(!handler.Bool(true)))
+                RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+        }
+        else
+            RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+    }
+
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseFalse(InputStream& is, Handler& handler) {
+        RAPIDJSON_ASSERT(is.Peek() == 'f');
+        is.Take();
+
+        if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) {
+            if (RAPIDJSON_UNLIKELY(!handler.Bool(false)))
+                RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+        }
+        else
+            RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+    }
+
+    template<typename InputStream>
+    RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) {
+        if (RAPIDJSON_LIKELY(is.Peek() == expect)) {
+            is.Take();
+            return true;
+        }
+        else
+            return false;
+    }
+
+    // Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
+    template<typename InputStream>
+    unsigned ParseHex4(InputStream& is, size_t escapeOffset) {
+        unsigned codepoint = 0;
+        for (int i = 0; i < 4; i++) {
+            Ch c = is.Peek();
+            codepoint <<= 4;
+            codepoint += static_cast<unsigned>(c);
+            if (c >= '0' && c <= '9')
+                codepoint -= '0';
+            else if (c >= 'A' && c <= 'F')
+                codepoint -= 'A' - 10;
+            else if (c >= 'a' && c <= 'f')
+                codepoint -= 'a' - 10;
+            else {
+                RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset);
+                RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
+            }
+            is.Take();
+        }
+        return codepoint;
+    }
+
+    template <typename CharType>
+    class StackStream {
+    public:
+        typedef CharType Ch;
+
+        StackStream(internal::Stack<StackAllocator>& stack) : stack_(stack), length_(0) {}
+        RAPIDJSON_FORCEINLINE void Put(Ch c) {
+            *stack_.template Push<Ch>() = c;
+            ++length_;
+        }
+
+        RAPIDJSON_FORCEINLINE void* Push(SizeType count) {
+            length_ += count;
+            return stack_.template Push<Ch>(count);
+        }
+
+        size_t Length() const { return length_; }
+
+        Ch* Pop() {
+            return stack_.template Pop<Ch>(length_);
+        }
+
+    private:
+        StackStream(const StackStream&);
+        StackStream& operator=(const StackStream&);
+
+        internal::Stack<StackAllocator>& stack_;
+        SizeType length_;
+    };
+
+    // Parse string and generate String event. Different code paths for kParseInsituFlag.
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseString(InputStream& is, Handler& handler, bool isKey = false) {
+        internal::StreamLocalCopy<InputStream> copy(is);
+        InputStream& s(copy.s);
+
+        RAPIDJSON_ASSERT(s.Peek() == '\"');
+        s.Take();  // Skip '\"'
+
+        bool success = false;
+        if (parseFlags & kParseInsituFlag) {
+            typename InputStream::Ch *head = s.PutBegin();
+            ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+            size_t length = s.PutEnd(head) - 1;
+            RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+            const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+            success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false));
+        }
+        else {
+            StackStream<typename TargetEncoding::Ch> stackStream(stack_);
+            ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+            SizeType length = static_cast<SizeType>(stackStream.Length()) - 1;
+            const typename TargetEncoding::Ch* const str = stackStream.Pop();
+            success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true));
+        }
+        if (RAPIDJSON_UNLIKELY(!success))
+            RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
+    }
+
+    // Parse string to an output is
+    // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation.
+    template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream>
+    RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) {
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+        static const char escape[256] = {
+            Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
+            Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
+            0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
+            0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16
+        };
+#undef Z16
+//!@endcond
+
+        for (;;) {
+            // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation.
+            if (!(parseFlags & kParseValidateEncodingFlag))
+                ScanCopyUnescapedString(is, os);
+
+            Ch c = is.Peek();
+            if (RAPIDJSON_UNLIKELY(c == '\\')) {    // Escape
+                size_t escapeOffset = is.Tell();    // For invalid escaping, report the inital '\\' as error offset
+                is.Take();
+                Ch e = is.Peek();
+                if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) {
+                    is.Take();
+                    os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)]));
+                }
+                else if (RAPIDJSON_LIKELY(e == 'u')) {    // Unicode
+                    is.Take();
+                    unsigned codepoint = ParseHex4(is, escapeOffset);
+                    RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+                    if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) {
+                        // Handle UTF-16 surrogate pair
+                        if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
+                            RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+                        unsigned codepoint2 = ParseHex4(is, escapeOffset);
+                        RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+                        if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
+                            RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+                        codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
+                    }
+                    TEncoding::Encode(os, codepoint);
+                }
+                else
+                    RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset);
+            }
+            else if (RAPIDJSON_UNLIKELY(c == '"')) {    // Closing double quote
+                is.Take();
+                os.Put('\0');   // null-terminate the string
+                return;
+            }
+            else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+                if (c == '\0')
+                    RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell());
+                else
+                    RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell());
+            }
+            else {
+                size_t offset = is.Tell();
+                if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ?
+                    !Transcoder<SEncoding, TEncoding>::Validate(is, os) :
+                    !Transcoder<SEncoding, TEncoding>::Transcode(is, os))))
+                    RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset);
+            }
+        }
+    }
+
+    template<typename InputStream, typename OutputStream>
+    static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) {
+            // Do nothing for generic version
+    }
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+    // StringStream -> StackStream<char>
+    static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) {
+        const char* p = is.src_;
+
+        // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+        const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+        while (p != nextAligned)
+            if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+                is.src_ = p;
+                return;
+            }
+            else
+                os.Put(*p++);
+
+        // The rest of string using SIMD
+        static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+        static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+        static const char space[16]  = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };
+        const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+        const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+        const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+        for (;; p += 16) {
+            const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+            const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+            const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+            const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F
+            const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+            unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+            if (RAPIDJSON_UNLIKELY(r != 0)) {   // some of characters is escaped
+                SizeType length;
+    #ifdef _MSC_VER         // Find the index of first escaped
+                unsigned long offset;
+                _BitScanForward(&offset, r);
+                length = offset;
+    #else
+                length = static_cast<SizeType>(__builtin_ffs(r) - 1);
+    #endif
+                if (length != 0) {
+                    char* q = reinterpret_cast<char*>(os.Push(length));
+                    for (size_t i = 0; i < length; i++)
+                        q[i] = p[i];
+
+                    p += length;
+                }
+                break;
+            }
+            _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s);
+        }
+
+        is.src_ = p;
+    }
+
+    // InsituStringStream -> InsituStringStream
+    static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) {
+        RAPIDJSON_ASSERT(&is == &os);
+        (void)os;
+
+        if (is.src_ == is.dst_) {
+            SkipUnescapedString(is);
+            return;
+        }
+
+        char* p = is.src_;
+        char *q = is.dst_;
+
+        // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+        const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+        while (p != nextAligned)
+            if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+                is.src_ = p;
+                is.dst_ = q;
+                return;
+            }
+            else
+                *q++ = *p++;
+
+        // The rest of string using SIMD
+        static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+        static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+        static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };
+        const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+        const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+        const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+        for (;; p += 16, q += 16) {
+            const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+            const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+            const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+            const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F
+            const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+            unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+            if (RAPIDJSON_UNLIKELY(r != 0)) {   // some of characters is escaped
+                size_t length;
+#ifdef _MSC_VER         // Find the index of first escaped
+                unsigned long offset;
+                _BitScanForward(&offset, r);
+                length = offset;
+#else
+                length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+                for (const char* pend = p + length; p != pend; )
+                    *q++ = *p++;
+                break;
+            }
+            _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s);
+        }
+
+        is.src_ = p;
+        is.dst_ = q;
+    }
+
+    // When read/write pointers are the same for insitu stream, just skip unescaped characters
+    static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) {
+        RAPIDJSON_ASSERT(is.src_ == is.dst_);
+        char* p = is.src_;
+
+        // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+        const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+        for (; p != nextAligned; p++)
+            if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+                is.src_ = is.dst_ = p;
+                return;
+            }
+
+        // The rest of string using SIMD
+        static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+        static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+        static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };
+        const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+        const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+        const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+        for (;; p += 16) {
+            const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+            const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+            const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+            const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F
+            const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+            unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+            if (RAPIDJSON_UNLIKELY(r != 0)) {   // some of characters is escaped
+                size_t length;
+#ifdef _MSC_VER         // Find the index of first escaped
+                unsigned long offset;
+                _BitScanForward(&offset, r);
+                length = offset;
+#else
+                length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+                p += length;
+                break;
+            }
+        }
+
+        is.src_ = is.dst_ = p;
+    }
+#elif defined(RAPIDJSON_NEON)
+    // StringStream -> StackStream<char>
+    static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) {
+        const char* p = is.src_;
+
+        // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+        const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+        while (p != nextAligned)
+            if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+                is.src_ = p;
+                return;
+            }
+            else
+                os.Put(*p++);
+
+        // The rest of string using SIMD
+        const uint8x16_t s0 = vmovq_n_u8('"');
+        const uint8x16_t s1 = vmovq_n_u8('\\');
+        const uint8x16_t s2 = vmovq_n_u8('\b');
+        const uint8x16_t s3 = vmovq_n_u8(32);
+
+        for (;; p += 16) {
+            const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p));
+            uint8x16_t x = vceqq_u8(s, s0);
+            x = vorrq_u8(x, vceqq_u8(s, s1));
+            x = vorrq_u8(x, vceqq_u8(s, s2));
+            x = vorrq_u8(x, vcltq_u8(s, s3));
+
+            x = vrev64q_u8(x);                     // Rev in 64
+            uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
+            uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+
+            SizeType length = 0;
+            bool escaped = false;
+            if (low == 0) {
+                if (high != 0) {
+                    unsigned lz = (unsigned)__builtin_clzll(high);;
+                    length = 8 + (lz >> 3);
+                    escaped = true;
+                }
+            } else {
+                unsigned lz = (unsigned)__builtin_clzll(low);;
+                length = lz >> 3;
+                escaped = true;
+            }
+            if (RAPIDJSON_UNLIKELY(escaped)) {   // some of characters is escaped
+                if (length != 0) {
+                    char* q = reinterpret_cast<char*>(os.Push(length));
+                    for (size_t i = 0; i < length; i++)
+                        q[i] = p[i];
+
+                    p += length;
+                }
+                break;
+            }
+            vst1q_u8(reinterpret_cast<uint8_t *>(os.Push(16)), s);
+        }
+
+        is.src_ = p;
+    }
+
+    // InsituStringStream -> InsituStringStream
+    static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) {
+        RAPIDJSON_ASSERT(&is == &os);
+        (void)os;
+
+        if (is.src_ == is.dst_) {
+            SkipUnescapedString(is);
+            return;
+        }
+
+        char* p = is.src_;
+        char *q = is.dst_;
+
+        // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+        const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+        while (p != nextAligned)
+            if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+                is.src_ = p;
+                is.dst_ = q;
+                return;
+            }
+            else
+                *q++ = *p++;
+
+        // The rest of string using SIMD
+        const uint8x16_t s0 = vmovq_n_u8('"');
+        const uint8x16_t s1 = vmovq_n_u8('\\');
+        const uint8x16_t s2 = vmovq_n_u8('\b');
+        const uint8x16_t s3 = vmovq_n_u8(32);
+
+        for (;; p += 16, q += 16) {
+            const uint8x16_t s = vld1q_u8(reinterpret_cast<uint8_t *>(p));
+            uint8x16_t x = vceqq_u8(s, s0);
+            x = vorrq_u8(x, vceqq_u8(s, s1));
+            x = vorrq_u8(x, vceqq_u8(s, s2));
+            x = vorrq_u8(x, vcltq_u8(s, s3));
+
+            x = vrev64q_u8(x);                     // Rev in 64
+            uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
+            uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+
+            SizeType length = 0;
+            bool escaped = false;
+            if (low == 0) {
+                if (high != 0) {
+                    unsigned lz = (unsigned)__builtin_clzll(high);
+                    length = 8 + (lz >> 3);
+                    escaped = true;
+                }
+            } else {
+                unsigned lz = (unsigned)__builtin_clzll(low);
+                length = lz >> 3;
+                escaped = true;
+            }
+            if (RAPIDJSON_UNLIKELY(escaped)) {   // some of characters is escaped
+                for (const char* pend = p + length; p != pend; ) {
+                    *q++ = *p++;
+                }
+                break;
+            }
+            vst1q_u8(reinterpret_cast<uint8_t *>(q), s);
+        }
+
+        is.src_ = p;
+        is.dst_ = q;
+    }
+
+    // When read/write pointers are the same for insitu stream, just skip unescaped characters
+    static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) {
+        RAPIDJSON_ASSERT(is.src_ == is.dst_);
+        char* p = is.src_;
+
+        // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+        const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+        for (; p != nextAligned; p++)
+            if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+                is.src_ = is.dst_ = p;
+                return;
+            }
+
+        // The rest of string using SIMD
+        const uint8x16_t s0 = vmovq_n_u8('"');
+        const uint8x16_t s1 = vmovq_n_u8('\\');
+        const uint8x16_t s2 = vmovq_n_u8('\b');
+        const uint8x16_t s3 = vmovq_n_u8(32);
+
+        for (;; p += 16) {
+            const uint8x16_t s = vld1q_u8(reinterpret_cast<uint8_t *>(p));
+            uint8x16_t x = vceqq_u8(s, s0);
+            x = vorrq_u8(x, vceqq_u8(s, s1));
+            x = vorrq_u8(x, vceqq_u8(s, s2));
+            x = vorrq_u8(x, vcltq_u8(s, s3));
+
+            x = vrev64q_u8(x);                     // Rev in 64
+            uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
+            uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+
+            if (low == 0) {
+                if (high != 0) {
+                    int lz = __builtin_clzll(high);
+                    p += 8 + (lz >> 3);
+                    break;
+                }
+            } else {
+                int lz = __builtin_clzll(low);
+                p += lz >> 3;
+                break;
+            }
+        }
+
+        is.src_ = is.dst_ = p;
+    }
+#endif // RAPIDJSON_NEON
+
+    template<typename InputStream, bool backup, bool pushOnTake>
+    class NumberStream;
+
+    template<typename InputStream>
+    class NumberStream<InputStream, false, false> {
+    public:
+        typedef typename InputStream::Ch Ch;
+
+        NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader;  }
+
+        RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
+        RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
+        RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
+                 RAPIDJSON_FORCEINLINE void Push(char) {}
+
+        size_t Tell() { return is.Tell(); }
+        size_t Length() { return 0; }
+        const char* Pop() { return 0; }
+
+    protected:
+        NumberStream& operator=(const NumberStream&);
+
+        InputStream& is;
+    };
+
+    template<typename InputStream>
+    class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> {
+        typedef NumberStream<InputStream, false, false> Base;
+    public:
+        NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {}
+
+        RAPIDJSON_FORCEINLINE Ch TakePush() {
+            stackStream.Put(static_cast<char>(Base::is.Peek()));
+            return Base::is.Take();
+        }
+
+        RAPIDJSON_FORCEINLINE void Push(char c) {
+            stackStream.Put(c);
+        }
+
+        size_t Length() { return stackStream.Length(); }
+
+        const char* Pop() {
+            stackStream.Put('\0');
+            return stackStream.Pop();
+        }
+
+    private:
+        StackStream<char> stackStream;
+    };
+
+    template<typename InputStream>
+    class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> {
+        typedef NumberStream<InputStream, true, false> Base;
+    public:
+        NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {}
+
+        RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); }
+    };
+
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseNumber(InputStream& is, Handler& handler) {
+        internal::StreamLocalCopy<InputStream> copy(is);
+        NumberStream<InputStream,
+            ((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
+                ((parseFlags & kParseInsituFlag) == 0) :
+                ((parseFlags & kParseFullPrecisionFlag) != 0),
+            (parseFlags & kParseNumbersAsStringsFlag) != 0 &&
+                (parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
+
+        size_t startOffset = s.Tell();
+        double d = 0.0;
+        bool useNanOrInf = false;
+
+        // Parse minus
+        bool minus = Consume(s, '-');
+
+        // Parse int: zero / ( digit1-9 *DIGIT )
+        unsigned i = 0;
+        uint64_t i64 = 0;
+        bool use64bit = false;
+        int significandDigit = 0;
+        if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) {
+            i = 0;
+            s.TakePush();
+        }
+        else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) {
+            i = static_cast<unsigned>(s.TakePush() - '0');
+
+            if (minus)
+                while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                    if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648
+                        if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) {
+                            i64 = i;
+                            use64bit = true;
+                            break;
+                        }
+                    }
+                    i = i * 10 + static_cast<unsigned>(s.TakePush() - '0');
+                    significandDigit++;
+                }
+            else
+                while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                    if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295
+                        if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) {
+                            i64 = i;
+                            use64bit = true;
+                            break;
+                        }
+                    }
+                    i = i * 10 + static_cast<unsigned>(s.TakePush() - '0');
+                    significandDigit++;
+                }
+        }
+        // Parse NaN or Infinity here
+        else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) {
+            if (Consume(s, 'N')) {
+                if (Consume(s, 'a') && Consume(s, 'N')) {
+                    d = std::numeric_limits<double>::quiet_NaN();
+                    useNanOrInf = true;
+                }
+            }
+            else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) {
+                if (Consume(s, 'n') && Consume(s, 'f')) {
+                    d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity());
+                    useNanOrInf = true;
+
+                    if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n')
+                                                                && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) {
+                        RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+                    }
+                }
+            }
+            
+            if (RAPIDJSON_UNLIKELY(!useNanOrInf)) {
+                RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+            }
+        }
+        else
+            RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+
+        // Parse 64bit int
+        bool useDouble = false;
+        if (use64bit) {
+            if (minus)
+                while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                     if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808
+                        if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) {
+                            d = static_cast<double>(i64);
+                            useDouble = true;
+                            break;
+                        }
+                    i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+                    significandDigit++;
+                }
+            else
+                while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                    if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615
+                        if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) {
+                            d = static_cast<double>(i64);
+                            useDouble = true;
+                            break;
+                        }
+                    i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+                    significandDigit++;
+                }
+        }
+
+        // Force double for big integer
+        if (useDouble) {
+            while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0
+                    RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
+                d = d * 10 + (s.TakePush() - '0');
+            }
+        }
+
+        // Parse frac = decimal-point 1*DIGIT
+        int expFrac = 0;
+        size_t decimalPosition;
+        if (Consume(s, '.')) {
+            decimalPosition = s.Length();
+
+            if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9')))
+                RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell());
+
+            if (!useDouble) {
+#if RAPIDJSON_64BIT
+                // Use i64 to store significand in 64-bit architecture
+                if (!use64bit)
+                    i64 = i;
+
+                while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                    if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path
+                        break;
+                    else {
+                        i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+                        --expFrac;
+                        if (i64 != 0)
+                            significandDigit++;
+                    }
+                }
+
+                d = static_cast<double>(i64);
+#else
+                // Use double to store significand in 32-bit architecture
+                d = static_cast<double>(use64bit ? i64 : i);
+#endif
+                useDouble = true;
+            }
+
+            while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                if (significandDigit < 17) {
+                    d = d * 10.0 + (s.TakePush() - '0');
+                    --expFrac;
+                    if (RAPIDJSON_LIKELY(d > 0.0))
+                        significandDigit++;
+                }
+                else
+                    s.TakePush();
+            }
+        }
+        else
+            decimalPosition = s.Length(); // decimal position at the end of integer.
+
+        // Parse exp = e [ minus / plus ] 1*DIGIT
+        int exp = 0;
+        if (Consume(s, 'e') || Consume(s, 'E')) {
+            if (!useDouble) {
+                d = static_cast<double>(use64bit ? i64 : i);
+                useDouble = true;
+            }
+
+            bool expMinus = false;
+            if (Consume(s, '+'))
+                ;
+            else if (Consume(s, '-'))
+                expMinus = true;
+
+            if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                exp = static_cast<int>(s.Take() - '0');
+                if (expMinus) {
+                    while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                        exp = exp * 10 + static_cast<int>(s.Take() - '0');
+                        if (exp >= 214748364) {                         // Issue #313: prevent overflow exponent
+                            while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9'))  // Consume the rest of exponent
+                                s.Take();
+                        }
+                    }
+                }
+                else {  // positive exp
+                    int maxExp = 308 - expFrac;
+                    while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+                        exp = exp * 10 + static_cast<int>(s.Take() - '0');
+                        if (RAPIDJSON_UNLIKELY(exp > maxExp))
+                            RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
+                    }
+                }
+            }
+            else
+                RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell());
+
+            if (expMinus)
+                exp = -exp;
+        }
+
+        // Finish parsing, call event according to the type of number.
+        bool cont = true;
+
+        if (parseFlags & kParseNumbersAsStringsFlag) {
+            if (parseFlags & kParseInsituFlag) {
+                s.Pop();  // Pop stack no matter if it will be used or not.
+                typename InputStream::Ch* head = is.PutBegin();
+                const size_t length = s.Tell() - startOffset;
+                RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+                // unable to insert the \0 character here, it will erase the comma after this number
+                const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+                cont = handler.RawNumber(str, SizeType(length), false);
+            }
+            else {
+                SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
+                StringStream srcStream(s.Pop());
+                StackStream<typename TargetEncoding::Ch> dstStream(stack_);
+                while (numCharsToCopy--) {
+                    Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream);
+                }
+                dstStream.Put('\0');
+                const typename TargetEncoding::Ch* str = dstStream.Pop();
+                const SizeType length = static_cast<SizeType>(dstStream.Length()) - 1;
+                cont = handler.RawNumber(str, SizeType(length), true);
+            }
+        }
+        else {
+           size_t length = s.Length();
+           const char* decimal = s.Pop();  // Pop stack no matter if it will be used or not.
+
+           if (useDouble) {
+               int p = exp + expFrac;
+               if (parseFlags & kParseFullPrecisionFlag)
+                   d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp);
+               else
+                   d = internal::StrtodNormalPrecision(d, p);
+
+               cont = handler.Double(minus ? -d : d);
+           }
+           else if (useNanOrInf) {
+               cont = handler.Double(d);
+           }
+           else {
+               if (use64bit) {
+                   if (minus)
+                       cont = handler.Int64(static_cast<int64_t>(~i64 + 1));
+                   else
+                       cont = handler.Uint64(i64);
+               }
+               else {
+                   if (minus)
+                       cont = handler.Int(static_cast<int32_t>(~i + 1));
+                   else
+                       cont = handler.Uint(i);
+               }
+           }
+        }
+        if (RAPIDJSON_UNLIKELY(!cont))
+            RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset);
+    }
+
+    // Parse any JSON value
+    template<unsigned parseFlags, typename InputStream, typename Handler>
+    void ParseValue(InputStream& is, Handler& handler) {
+        switch (is.Peek()) {
+            case 'n': ParseNull  <parseFlags>(is, handler); break;
+            case 't': ParseTrue  <parseFlags>(is, handler); break;
+            case 'f': ParseFalse <parseFlags>(is, handler); break;
+            case '"': ParseString<parseFlags>(is, handler); break;
+            case '{': ParseObject<parseFlags>(is, handler); break;
+            case '[': ParseArray <parseFlags>(is, handler); break;
+            default :
+                      ParseNumber<parseFlags>(is, handler);
+                      break;
+
+        }
+    }
+
+    // Iterative Parsing
+
+    // States
+    enum IterativeParsingState {
+        IterativeParsingFinishState = 0, // sink states at top
+        IterativeParsingErrorState,      // sink states at top
+        IterativeParsingStartState,
+
+        // Object states
+        IterativeParsingObjectInitialState,
+        IterativeParsingMemberKeyState,
+        IterativeParsingMemberValueState,
+        IterativeParsingObjectFinishState,
+
+        // Array states
+        IterativeParsingArrayInitialState,
+        IterativeParsingElementState,
+        IterativeParsingArrayFinishState,
+
+        // Single value state
+        IterativeParsingValueState,
+        
+        // Delimiter states (at bottom)
+        IterativeParsingElementDelimiterState,
+        IterativeParsingMemberDelimiterState,
+        IterativeParsingKeyValueDelimiterState,
+        
+        cIterativeParsingStateCount
+    };
+
+    // Tokens
+    enum Token {
+        LeftBracketToken = 0,
+        RightBracketToken,
+
+        LeftCurlyBracketToken,
+        RightCurlyBracketToken,
+
+        CommaToken,
+        ColonToken,
+
+        StringToken,
+        FalseToken,
+        TrueToken,
+        NullToken,
+        NumberToken,
+
+        kTokenCount
+    };
+
+    RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) {
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define N NumberToken
+#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N
+        // Maps from ASCII to Token
+        static const unsigned char tokenMap[256] = {
+            N16, // 00~0F
+            N16, // 10~1F
+            N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F
+            N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F
+            N16, // 40~4F
+            N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F
+            N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F
+            N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F
+            N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF
+        };
+#undef N
+#undef N16
+//!@endcond
+
+        if (sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256)
+            return static_cast<Token>(tokenMap[static_cast<unsigned char>(c)]);
+        else
+            return NumberToken;
+    }
+
+    RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) {
+        // current state x one lookahead token -> new state
+        static const char G[cIterativeParsingStateCount][kTokenCount] = {
+            // Finish(sink state)
+            {
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState
+            },
+            // Error(sink state)
+            {
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState
+            },
+            // Start
+            {
+                IterativeParsingArrayInitialState,  // Left bracket
+                IterativeParsingErrorState,         // Right bracket
+                IterativeParsingObjectInitialState, // Left curly bracket
+                IterativeParsingErrorState,         // Right curly bracket
+                IterativeParsingErrorState,         // Comma
+                IterativeParsingErrorState,         // Colon
+                IterativeParsingValueState,         // String
+                IterativeParsingValueState,         // False
+                IterativeParsingValueState,         // True
+                IterativeParsingValueState,         // Null
+                IterativeParsingValueState          // Number
+            },
+            // ObjectInitial
+            {
+                IterativeParsingErrorState,         // Left bracket
+                IterativeParsingErrorState,         // Right bracket
+                IterativeParsingErrorState,         // Left curly bracket
+                IterativeParsingObjectFinishState,  // Right curly bracket
+                IterativeParsingErrorState,         // Comma
+                IterativeParsingErrorState,         // Colon
+                IterativeParsingMemberKeyState,     // String
+                IterativeParsingErrorState,         // False
+                IterativeParsingErrorState,         // True
+                IterativeParsingErrorState,         // Null
+                IterativeParsingErrorState          // Number
+            },
+            // MemberKey
+            {
+                IterativeParsingErrorState,             // Left bracket
+                IterativeParsingErrorState,             // Right bracket
+                IterativeParsingErrorState,             // Left curly bracket
+                IterativeParsingErrorState,             // Right curly bracket
+                IterativeParsingErrorState,             // Comma
+                IterativeParsingKeyValueDelimiterState, // Colon
+                IterativeParsingErrorState,             // String
+                IterativeParsingErrorState,             // False
+                IterativeParsingErrorState,             // True
+                IterativeParsingErrorState,             // Null
+                IterativeParsingErrorState              // Number
+            },
+            // MemberValue
+            {
+                IterativeParsingErrorState,             // Left bracket
+                IterativeParsingErrorState,             // Right bracket
+                IterativeParsingErrorState,             // Left curly bracket
+                IterativeParsingObjectFinishState,      // Right curly bracket
+                IterativeParsingMemberDelimiterState,   // Comma
+                IterativeParsingErrorState,             // Colon
+                IterativeParsingErrorState,             // String
+                IterativeParsingErrorState,             // False
+                IterativeParsingErrorState,             // True
+                IterativeParsingErrorState,             // Null
+                IterativeParsingErrorState              // Number
+            },
+            // ObjectFinish(sink state)
+            {
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState
+            },
+            // ArrayInitial
+            {
+                IterativeParsingArrayInitialState,      // Left bracket(push Element state)
+                IterativeParsingArrayFinishState,       // Right bracket
+                IterativeParsingObjectInitialState,     // Left curly bracket(push Element state)
+                IterativeParsingErrorState,             // Right curly bracket
+                IterativeParsingErrorState,             // Comma
+                IterativeParsingErrorState,             // Colon
+                IterativeParsingElementState,           // String
+                IterativeParsingElementState,           // False
+                IterativeParsingElementState,           // True
+                IterativeParsingElementState,           // Null
+                IterativeParsingElementState            // Number
+            },
+            // Element
+            {
+                IterativeParsingErrorState,             // Left bracket
+                IterativeParsingArrayFinishState,       // Right bracket
+                IterativeParsingErrorState,             // Left curly bracket
+                IterativeParsingErrorState,             // Right curly bracket
+                IterativeParsingElementDelimiterState,  // Comma
+                IterativeParsingErrorState,             // Colon
+                IterativeParsingErrorState,             // String
+                IterativeParsingErrorState,             // False
+                IterativeParsingErrorState,             // True
+                IterativeParsingErrorState,             // Null
+                IterativeParsingErrorState              // Number
+            },
+            // ArrayFinish(sink state)
+            {
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState
+            },
+            // Single Value (sink state)
+            {
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+                IterativeParsingErrorState
+            },
+            // ElementDelimiter
+            {
+                IterativeParsingArrayInitialState,      // Left bracket(push Element state)
+                IterativeParsingArrayFinishState,       // Right bracket
+                IterativeParsingObjectInitialState,     // Left curly bracket(push Element state)
+                IterativeParsingErrorState,             // Right curly bracket
+                IterativeParsingErrorState,             // Comma
+                IterativeParsingErrorState,             // Colon
+                IterativeParsingElementState,           // String
+                IterativeParsingElementState,           // False
+                IterativeParsingElementState,           // True
+                IterativeParsingElementState,           // Null
+                IterativeParsingElementState            // Number
+            },
+            // MemberDelimiter
+            {
+                IterativeParsingErrorState,         // Left bracket
+                IterativeParsingErrorState,         // Right bracket
+                IterativeParsingErrorState,         // Left curly bracket
+                IterativeParsingObjectFinishState,  // Right curly bracket
+                IterativeParsingErrorState,         // Comma
+                IterativeParsingErrorState,         // Colon
+                IterativeParsingMemberKeyState,     // String
+                IterativeParsingErrorState,         // False
+                IterativeParsingErrorState,         // True
+                IterativeParsingErrorState,         // Null
+                IterativeParsingErrorState          // Number
+            },
+            // KeyValueDelimiter
+            {
+                IterativeParsingArrayInitialState,      // Left bracket(push MemberValue state)
+                IterativeParsingErrorState,             // Right bracket
+                IterativeParsingObjectInitialState,     // Left curly bracket(push MemberValue state)
+                IterativeParsingErrorState,             // Right curly bracket
+                IterativeParsingErrorState,             // Comma
+                IterativeParsingErrorState,             // Colon
+                IterativeParsingMemberValueState,       // String
+                IterativeParsingMemberValueState,       // False
+                IterativeParsingMemberValueState,       // True
+                IterativeParsingMemberValueState,       // Null
+                IterativeParsingMemberValueState        // Number
+            },
+        }; // End of G
+
+        return static_cast<IterativeParsingState>(G[state][token]);
+    }
+
+    // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit().
+    // May return a new state on state pop.
+    template <unsigned parseFlags, typename InputStream, typename Handler>
+    RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
+        (void)token;
+
+        switch (dst) {
+        case IterativeParsingErrorState:
+            return dst;
+
+        case IterativeParsingObjectInitialState:
+        case IterativeParsingArrayInitialState:
+        {
+            // Push the state(Element or MemeberValue) if we are nested in another array or value of member.
+            // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop.
+            IterativeParsingState n = src;
+            if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState)
+                n = IterativeParsingElementState;
+            else if (src == IterativeParsingKeyValueDelimiterState)
+                n = IterativeParsingMemberValueState;
+            // Push current state.
+            *stack_.template Push<SizeType>(1) = n;
+            // Initialize and push the member/element count.
+            *stack_.template Push<SizeType>(1) = 0;
+            // Call handler
+            bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray();
+            // On handler short circuits the parsing.
+            if (!hr) {
+                RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+                return IterativeParsingErrorState;
+            }
+            else {
+                is.Take();
+                return dst;
+            }
+        }
+
+        case IterativeParsingMemberKeyState:
+            ParseString<parseFlags>(is, handler, true);
+            if (HasParseError())
+                return IterativeParsingErrorState;
+            else
+                return dst;
+
+        case IterativeParsingKeyValueDelimiterState:
+            RAPIDJSON_ASSERT(token == ColonToken);
+            is.Take();
+            return dst;
+
+        case IterativeParsingMemberValueState:
+            // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+            ParseValue<parseFlags>(is, handler);
+            if (HasParseError()) {
+                return IterativeParsingErrorState;
+            }
+            return dst;
+
+        case IterativeParsingElementState:
+            // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+            ParseValue<parseFlags>(is, handler);
+            if (HasParseError()) {
+                return IterativeParsingErrorState;
+            }
+            return dst;
+
+        case IterativeParsingMemberDelimiterState:
+        case IterativeParsingElementDelimiterState:
+            is.Take();
+            // Update member/element count.
+            *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1;
+            return dst;
+
+        case IterativeParsingObjectFinishState:
+        {
+            // Transit from delimiter is only allowed when trailing commas are enabled
+            if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) {
+                RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell());
+                return IterativeParsingErrorState;
+            }
+            // Get member count.
+            SizeType c = *stack_.template Pop<SizeType>(1);
+            // If the object is not empty, count the last member.
+            if (src == IterativeParsingMemberValueState)
+                ++c;
+            // Restore the state.
+            IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
+            // Transit to Finish state if this is the topmost scope.
+            if (n == IterativeParsingStartState)
+                n = IterativeParsingFinishState;
+            // Call handler
+            bool hr = handler.EndObject(c);
+            // On handler short circuits the parsing.
+            if (!hr) {
+                RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+                return IterativeParsingErrorState;
+            }
+            else {
+                is.Take();
+                return n;
+            }
+        }
+
+        case IterativeParsingArrayFinishState:
+        {
+            // Transit from delimiter is only allowed when trailing commas are enabled
+            if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) {
+                RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell());
+                return IterativeParsingErrorState;
+            }
+            // Get element count.
+            SizeType c = *stack_.template Pop<SizeType>(1);
+            // If the array is not empty, count the last element.
+            if (src == IterativeParsingElementState)
+                ++c;
+            // Restore the state.
+            IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
+            // Transit to Finish state if this is the topmost scope.
+            if (n == IterativeParsingStartState)
+                n = IterativeParsingFinishState;
+            // Call handler
+            bool hr = handler.EndArray(c);
+            // On handler short circuits the parsing.
+            if (!hr) {
+                RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+                return IterativeParsingErrorState;
+            }
+            else {
+                is.Take();
+                return n;
+            }
+        }
+
+        default:
+            // This branch is for IterativeParsingValueState actually.
+            // Use `default:` rather than
+            // `case IterativeParsingValueState:` is for code coverage.
+
+            // The IterativeParsingStartState is not enumerated in this switch-case.
+            // It is impossible for that case. And it can be caught by following assertion.
+
+            // The IterativeParsingFinishState is not enumerated in this switch-case either.
+            // It is a "derivative" state which cannot triggered from Predict() directly.
+            // Therefore it cannot happen here. And it can be caught by following assertion.
+            RAPIDJSON_ASSERT(dst == IterativeParsingValueState);
+
+            // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+            ParseValue<parseFlags>(is, handler);
+            if (HasParseError()) {
+                return IterativeParsingErrorState;
+            }
+            return IterativeParsingFinishState;
+        }
+    }
+
+    template <typename InputStream>
+    void HandleError(IterativeParsingState src, InputStream& is) {
+        if (HasParseError()) {
+            // Error flag has been set.
+            return;
+        }
+
+        switch (src) {
+        case IterativeParsingStartState:            RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return;
+        case IterativeParsingFinishState:           RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return;
+        case IterativeParsingObjectInitialState:
+        case IterativeParsingMemberDelimiterState:  RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return;
+        case IterativeParsingMemberKeyState:        RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return;
+        case IterativeParsingMemberValueState:      RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return;
+        case IterativeParsingKeyValueDelimiterState:
+        case IterativeParsingArrayInitialState:
+        case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return;
+        default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return;
+        }
+    }
+
+    RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) {
+        return s >= IterativeParsingElementDelimiterState;
+    }
+    
+    RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) {
+        return s <= IterativeParsingErrorState;
+    }
+    
+    template <unsigned parseFlags, typename InputStream, typename Handler>
+    ParseResult IterativeParse(InputStream& is, Handler& handler) {
+        parseResult_.Clear();
+        ClearStackOnExit scope(*this);
+        IterativeParsingState state = IterativeParsingStartState;
+        
+        SkipWhitespaceAndComments<parseFlags>(is);
+        RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+        while (is.Peek() != '\0') {
+            Token t = Tokenize(is.Peek());
+            IterativeParsingState n = Predict(state, t);
+            IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler);
+            
+            if (d == IterativeParsingErrorState) {
+                HandleError(state, is);
+                break;
+            }
+            
+            state = d;
+            
+            // Do not further consume streams if a root JSON has been parsed.
+            if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState)
+                break;
+            
+            SkipWhitespaceAndComments<parseFlags>(is);
+            RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+        }
+        
+        // Handle the end of file.
+        if (state != IterativeParsingFinishState)
+            HandleError(state, is);
+        
+        return parseResult_;
+    }
+
+    static const size_t kDefaultStackCapacity = 256;    //!< Default stack capacity in bytes for storing a single decoded string.
+    internal::Stack<StackAllocator> stack_;  //!< A stack for storing decoded string temporarily during non-destructive parsing.
+    ParseResult parseResult_;
+    IterativeParsingState state_;
+}; // class GenericReader
+
+//! Reader with UTF8 encoding and default allocator.
+typedef GenericReader<UTF8<>, UTF8<> > Reader;
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_READER_H_
diff --git a/src/lottie/rapidjson/schema.h b/src/lottie/rapidjson/schema.h
new file mode 100644 (file)
index 0000000..2713fb2
--- /dev/null
@@ -0,0 +1,2017 @@
+// Tencent is pleased to support the open source community by making RapidJSON available->
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License-> You may obtain a copy of the License at
+//
+// http://opensource->org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied-> See the License for the 
+// specific language governing permissions and limitations under the License->
+
+#ifndef RAPIDJSON_SCHEMA_H_
+#define RAPIDJSON_SCHEMA_H_
+
+#include "document.h"
+#include "pointer.h"
+#include <cmath> // abs, floor
+
+#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
+#endif
+
+#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+#include "internal/regex.h"
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+#include <regex>
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
+#define RAPIDJSON_SCHEMA_HAS_REGEX 1
+#else
+#define RAPIDJSON_SCHEMA_HAS_REGEX 0
+#endif
+
+#ifndef RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#endif
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#include "stringbuffer.h"
+#endif
+
+RAPIDJSON_DIAG_PUSH
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(weak-vtables)
+RAPIDJSON_DIAG_OFF(exit-time-destructors)
+RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Verbose Utilities
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+
+namespace internal {
+
+inline void PrintInvalidKeyword(const char* keyword) {
+    printf("Fail keyword: %s\n", keyword);
+}
+
+inline void PrintInvalidKeyword(const wchar_t* keyword) {
+    wprintf(L"Fail keyword: %ls\n", keyword);
+}
+
+inline void PrintInvalidDocument(const char* document) {
+    printf("Fail document: %s\n\n", document);
+}
+
+inline void PrintInvalidDocument(const wchar_t* document) {
+    wprintf(L"Fail document: %ls\n\n", document);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
+    printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
+    wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
+}
+
+} // namespace internal
+
+#endif // RAPIDJSON_SCHEMA_VERBOSE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_INVALID_KEYWORD_RETURN
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
+#else
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
+#endif
+
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+    context.invalidKeyword = keyword.GetString();\
+    RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
+    return false;\
+RAPIDJSON_MULTILINEMACRO_END
+
+///////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+
+template <typename ValueType, typename Allocator>
+class GenericSchemaDocument;
+
+namespace internal {
+
+template <typename SchemaDocumentType>
+class Schema;
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaValidator
+
+class ISchemaValidator {
+public:
+    virtual ~ISchemaValidator() {}
+    virtual bool IsValid() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaStateFactory
+
+template <typename SchemaType>
+class ISchemaStateFactory {
+public:
+    virtual ~ISchemaStateFactory() {}
+    virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
+    virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
+    virtual void* CreateHasher() = 0;
+    virtual uint64_t GetHashCode(void* hasher) = 0;
+    virtual void DestroryHasher(void* hasher) = 0;
+    virtual void* MallocState(size_t size) = 0;
+    virtual void FreeState(void* p) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// For comparison of compound value
+template<typename Encoding, typename Allocator>
+class Hasher {
+public:
+    typedef typename Encoding::Ch Ch;
+
+    Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
+
+    bool Null() { return WriteType(kNullType); }
+    bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
+    bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+    bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+    bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+    bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+    bool Double(double d) { 
+        Number n; 
+        if (d < 0) n.u.i = static_cast<int64_t>(d);
+        else       n.u.u = static_cast<uint64_t>(d); 
+        n.d = d;
+        return WriteNumber(n);
+    }
+
+    bool RawNumber(const Ch* str, SizeType len, bool) {
+        WriteBuffer(kNumberType, str, len * sizeof(Ch));
+        return true;
+    }
+
+    bool String(const Ch* str, SizeType len, bool) {
+        WriteBuffer(kStringType, str, len * sizeof(Ch));
+        return true;
+    }
+
+    bool StartObject() { return true; }
+    bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
+    bool EndObject(SizeType memberCount) { 
+        uint64_t h = Hash(0, kObjectType);
+        uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
+        for (SizeType i = 0; i < memberCount; i++)
+            h ^= Hash(kv[i * 2], kv[i * 2 + 1]);  // Use xor to achieve member order insensitive
+        *stack_.template Push<uint64_t>() = h;
+        return true;
+    }
+    
+    bool StartArray() { return true; }
+    bool EndArray(SizeType elementCount) { 
+        uint64_t h = Hash(0, kArrayType);
+        uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
+        for (SizeType i = 0; i < elementCount; i++)
+            h = Hash(h, e[i]); // Use hash to achieve element order sensitive
+        *stack_.template Push<uint64_t>() = h;
+        return true;
+    }
+
+    bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
+
+    uint64_t GetHashCode() const {
+        RAPIDJSON_ASSERT(IsValid());
+        return *stack_.template Top<uint64_t>();
+    }
+
+private:
+    static const size_t kDefaultSize = 256;
+    struct Number {
+        union U {
+            uint64_t u;
+            int64_t i;
+        }u;
+        double d;
+    };
+
+    bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
+    
+    bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
+    
+    bool WriteBuffer(Type type, const void* data, size_t len) {
+        // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
+        uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
+        const unsigned char* d = static_cast<const unsigned char*>(data);
+        for (size_t i = 0; i < len; i++)
+            h = Hash(h, d[i]);
+        *stack_.template Push<uint64_t>() = h;
+        return true;
+    }
+
+    static uint64_t Hash(uint64_t h, uint64_t d) {
+        static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
+        h ^= d;
+        h *= kPrime;
+        return h;
+    }
+
+    Stack<Allocator> stack_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidationContext
+
+template <typename SchemaDocumentType>
+struct SchemaValidationContext {
+    typedef Schema<SchemaDocumentType> SchemaType;
+    typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
+    typedef typename SchemaType::ValueType ValueType;
+    typedef typename ValueType::Ch Ch;
+
+    enum PatternValidatorType {
+        kPatternValidatorOnly,
+        kPatternValidatorWithProperty,
+        kPatternValidatorWithAdditionalProperty
+    };
+
+    SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
+        factory(f),
+        schema(s),
+        valueSchema(),
+        invalidKeyword(),
+        hasher(),
+        arrayElementHashCodes(),
+        validators(),
+        validatorCount(),
+        patternPropertiesValidators(),
+        patternPropertiesValidatorCount(),
+        patternPropertiesSchemas(),
+        patternPropertiesSchemaCount(),
+        valuePatternValidatorType(kPatternValidatorOnly),
+        propertyExist(),
+        inArray(false),
+        valueUniqueness(false),
+        arrayUniqueness(false)
+    {
+    }
+
+    ~SchemaValidationContext() {
+        if (hasher)
+            factory.DestroryHasher(hasher);
+        if (validators) {
+            for (SizeType i = 0; i < validatorCount; i++)
+                factory.DestroySchemaValidator(validators[i]);
+            factory.FreeState(validators);
+        }
+        if (patternPropertiesValidators) {
+            for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
+                factory.DestroySchemaValidator(patternPropertiesValidators[i]);
+            factory.FreeState(patternPropertiesValidators);
+        }
+        if (patternPropertiesSchemas)
+            factory.FreeState(patternPropertiesSchemas);
+        if (propertyExist)
+            factory.FreeState(propertyExist);
+    }
+
+    SchemaValidatorFactoryType& factory;
+    const SchemaType* schema;
+    const SchemaType* valueSchema;
+    const Ch* invalidKeyword;
+    void* hasher; // Only validator access
+    void* arrayElementHashCodes; // Only validator access this
+    ISchemaValidator** validators;
+    SizeType validatorCount;
+    ISchemaValidator** patternPropertiesValidators;
+    SizeType patternPropertiesValidatorCount;
+    const SchemaType** patternPropertiesSchemas;
+    SizeType patternPropertiesSchemaCount;
+    PatternValidatorType valuePatternValidatorType;
+    PatternValidatorType objectPatternValidatorType;
+    SizeType arrayElementIndex;
+    bool* propertyExist;
+    bool inArray;
+    bool valueUniqueness;
+    bool arrayUniqueness;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Schema
+
+template <typename SchemaDocumentType>
+class Schema {
+public:
+    typedef typename SchemaDocumentType::ValueType ValueType;
+    typedef typename SchemaDocumentType::AllocatorType AllocatorType;
+    typedef typename SchemaDocumentType::PointerType PointerType;
+    typedef typename ValueType::EncodingType EncodingType;
+    typedef typename EncodingType::Ch Ch;
+    typedef SchemaValidationContext<SchemaDocumentType> Context;
+    typedef Schema<SchemaDocumentType> SchemaType;
+    typedef GenericValue<EncodingType, AllocatorType> SValue;
+    friend class GenericSchemaDocument<ValueType, AllocatorType>;
+
+    Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
+        allocator_(allocator),
+        typeless_(schemaDocument->GetTypeless()),
+        enum_(),
+        enumCount_(),
+        not_(),
+        type_((1 << kTotalSchemaType) - 1), // typeless
+        validatorCount_(),
+        notValidatorIndex_(),
+        properties_(),
+        additionalPropertiesSchema_(),
+        patternProperties_(),
+        patternPropertyCount_(),
+        propertyCount_(),
+        minProperties_(),
+        maxProperties_(SizeType(~0)),
+        additionalProperties_(true),
+        hasDependencies_(),
+        hasRequired_(),
+        hasSchemaDependencies_(),
+        additionalItemsSchema_(),
+        itemsList_(),
+        itemsTuple_(),
+        itemsTupleCount_(),
+        minItems_(),
+        maxItems_(SizeType(~0)),
+        additionalItems_(true),
+        uniqueItems_(false),
+        pattern_(),
+        minLength_(0),
+        maxLength_(~SizeType(0)),
+        exclusiveMinimum_(false),
+        exclusiveMaximum_(false)
+    {
+        typedef typename SchemaDocumentType::ValueType ValueType;
+        typedef typename ValueType::ConstValueIterator ConstValueIterator;
+        typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
+
+        if (!value.IsObject())
+            return;
+
+        if (const ValueType* v = GetMember(value, GetTypeString())) {
+            type_ = 0;
+            if (v->IsString())
+                AddType(*v);
+            else if (v->IsArray())
+                for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
+                    AddType(*itr);
+        }
+
+        if (const ValueType* v = GetMember(value, GetEnumString()))
+            if (v->IsArray() && v->Size() > 0) {
+                enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
+                for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
+                    typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
+                    char buffer[256 + 24];
+                    MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
+                    EnumHasherType h(&hasherAllocator, 256);
+                    itr->Accept(h);
+                    enum_[enumCount_++] = h.GetHashCode();
+                }
+            }
+
+        if (schemaDocument) {
+            AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
+            AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
+            AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
+        }
+
+        if (const ValueType* v = GetMember(value, GetNotString())) {
+            schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
+            notValidatorIndex_ = validatorCount_;
+            validatorCount_++;
+        }
+
+        // Object
+
+        const ValueType* properties = GetMember(value, GetPropertiesString());
+        const ValueType* required = GetMember(value, GetRequiredString());
+        const ValueType* dependencies = GetMember(value, GetDependenciesString());
+        {
+            // Gather properties from properties/required/dependencies
+            SValue allProperties(kArrayType);
+
+            if (properties && properties->IsObject())
+                for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
+                    AddUniqueElement(allProperties, itr->name);
+            
+            if (required && required->IsArray())
+                for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+                    if (itr->IsString())
+                        AddUniqueElement(allProperties, *itr);
+
+            if (dependencies && dependencies->IsObject())
+                for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+                    AddUniqueElement(allProperties, itr->name);
+                    if (itr->value.IsArray())
+                        for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
+                            if (i->IsString())
+                                AddUniqueElement(allProperties, *i);
+                }
+
+            if (allProperties.Size() > 0) {
+                propertyCount_ = allProperties.Size();
+                properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
+                for (SizeType i = 0; i < propertyCount_; i++) {
+                    new (&properties_[i]) Property();
+                    properties_[i].name = allProperties[i];
+                    properties_[i].schema = typeless_;
+                }
+            }
+        }
+
+        if (properties && properties->IsObject()) {
+            PointerType q = p.Append(GetPropertiesString(), allocator_);
+            for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
+                SizeType index;
+                if (FindPropertyIndex(itr->name, &index))
+                    schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
+            }
+        }
+
+        if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
+            PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
+            patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
+            patternPropertyCount_ = 0;
+
+            for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
+                new (&patternProperties_[patternPropertyCount_]) PatternProperty();
+                patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
+                schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
+                patternPropertyCount_++;
+            }
+        }
+
+        if (required && required->IsArray())
+            for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+                if (itr->IsString()) {
+                    SizeType index;
+                    if (FindPropertyIndex(*itr, &index)) {
+                        properties_[index].required = true;
+                        hasRequired_ = true;
+                    }
+                }
+
+        if (dependencies && dependencies->IsObject()) {
+            PointerType q = p.Append(GetDependenciesString(), allocator_);
+            hasDependencies_ = true;
+            for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+                SizeType sourceIndex;
+                if (FindPropertyIndex(itr->name, &sourceIndex)) {
+                    if (itr->value.IsArray()) {
+                        properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
+                        std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
+                        for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
+                            SizeType targetIndex;
+                            if (FindPropertyIndex(*targetItr, &targetIndex))
+                                properties_[sourceIndex].dependencies[targetIndex] = true;
+                        }
+                    }
+                    else if (itr->value.IsObject()) {
+                        hasSchemaDependencies_ = true;
+                        schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
+                        properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
+                        validatorCount_++;
+                    }
+                }
+            }
+        }
+
+        if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
+            if (v->IsBool())
+                additionalProperties_ = v->GetBool();
+            else if (v->IsObject())
+                schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
+        }
+
+        AssignIfExist(minProperties_, value, GetMinPropertiesString());
+        AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
+
+        // Array
+        if (const ValueType* v = GetMember(value, GetItemsString())) {
+            PointerType q = p.Append(GetItemsString(), allocator_);
+            if (v->IsObject()) // List validation
+                schemaDocument->CreateSchema(&itemsList_, q, *v, document);
+            else if (v->IsArray()) { // Tuple validation
+                itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
+                SizeType index = 0;
+                for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
+                    schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
+            }
+        }
+
+        AssignIfExist(minItems_, value, GetMinItemsString());
+        AssignIfExist(maxItems_, value, GetMaxItemsString());
+
+        if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
+            if (v->IsBool())
+                additionalItems_ = v->GetBool();
+            else if (v->IsObject())
+                schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
+        }
+
+        AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
+
+        // String
+        AssignIfExist(minLength_, value, GetMinLengthString());
+        AssignIfExist(maxLength_, value, GetMaxLengthString());
+
+        if (const ValueType* v = GetMember(value, GetPatternString()))
+            pattern_ = CreatePattern(*v);
+
+        // Number
+        if (const ValueType* v = GetMember(value, GetMinimumString()))
+            if (v->IsNumber())
+                minimum_.CopyFrom(*v, *allocator_);
+
+        if (const ValueType* v = GetMember(value, GetMaximumString()))
+            if (v->IsNumber())
+                maximum_.CopyFrom(*v, *allocator_);
+
+        AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
+        AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
+
+        if (const ValueType* v = GetMember(value, GetMultipleOfString()))
+            if (v->IsNumber() && v->GetDouble() > 0.0)
+                multipleOf_.CopyFrom(*v, *allocator_);
+    }
+
+    ~Schema() {
+        AllocatorType::Free(enum_);
+        if (properties_) {
+            for (SizeType i = 0; i < propertyCount_; i++)
+                properties_[i].~Property();
+            AllocatorType::Free(properties_);
+        }
+        if (patternProperties_) {
+            for (SizeType i = 0; i < patternPropertyCount_; i++)
+                patternProperties_[i].~PatternProperty();
+            AllocatorType::Free(patternProperties_);
+        }
+        AllocatorType::Free(itemsTuple_);
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+        if (pattern_) {
+            pattern_->~RegexType();
+            AllocatorType::Free(pattern_);
+        }
+#endif
+    }
+
+    bool BeginValue(Context& context) const {
+        if (context.inArray) {
+            if (uniqueItems_)
+                context.valueUniqueness = true;
+
+            if (itemsList_)
+                context.valueSchema = itemsList_;
+            else if (itemsTuple_) {
+                if (context.arrayElementIndex < itemsTupleCount_)
+                    context.valueSchema = itemsTuple_[context.arrayElementIndex];
+                else if (additionalItemsSchema_)
+                    context.valueSchema = additionalItemsSchema_;
+                else if (additionalItems_)
+                    context.valueSchema = typeless_;
+                else
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
+            }
+            else
+                context.valueSchema = typeless_;
+
+            context.arrayElementIndex++;
+        }
+        return true;
+    }
+
+    RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
+        if (context.patternPropertiesValidatorCount > 0) {
+            bool otherValid = false;
+            SizeType count = context.patternPropertiesValidatorCount;
+            if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
+                otherValid = context.patternPropertiesValidators[--count]->IsValid();
+
+            bool patternValid = true;
+            for (SizeType i = 0; i < count; i++)
+                if (!context.patternPropertiesValidators[i]->IsValid()) {
+                    patternValid = false;
+                    break;
+                }
+
+            if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
+                if (!patternValid)
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+            }
+            else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
+                if (!patternValid || !otherValid)
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+            }
+            else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
+                RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+        }
+
+        if (enum_) {
+            const uint64_t h = context.factory.GetHashCode(context.hasher);
+            for (SizeType i = 0; i < enumCount_; i++)
+                if (enum_[i] == h)
+                    goto foundEnum;
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
+            foundEnum:;
+        }
+
+        if (allOf_.schemas)
+            for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
+                if (!context.validators[i]->IsValid())
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
+        
+        if (anyOf_.schemas) {
+            for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
+                if (context.validators[i]->IsValid())
+                    goto foundAny;
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
+            foundAny:;
+        }
+
+        if (oneOf_.schemas) {
+            bool oneValid = false;
+            for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
+                if (context.validators[i]->IsValid()) {
+                    if (oneValid)
+                        RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+                    else
+                        oneValid = true;
+                }
+            if (!oneValid)
+                RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+        }
+
+        if (not_ && context.validators[notValidatorIndex_]->IsValid())
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
+
+        return true;
+    }
+
+    bool Null(Context& context) const { 
+        if (!(type_ & (1 << kNullSchemaType)))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+        return CreateParallelValidator(context);
+    }
+    
+    bool Bool(Context& context, bool) const { 
+        if (!(type_ & (1 << kBooleanSchemaType)))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+        return CreateParallelValidator(context);
+    }
+
+    bool Int(Context& context, int i) const {
+        if (!CheckInt(context, i))
+            return false;
+        return CreateParallelValidator(context);
+    }
+
+    bool Uint(Context& context, unsigned u) const {
+        if (!CheckUint(context, u))
+            return false;
+        return CreateParallelValidator(context);
+    }
+
+    bool Int64(Context& context, int64_t i) const {
+        if (!CheckInt(context, i))
+            return false;
+        return CreateParallelValidator(context);
+    }
+
+    bool Uint64(Context& context, uint64_t u) const {
+        if (!CheckUint(context, u))
+            return false;
+        return CreateParallelValidator(context);
+    }
+
+    bool Double(Context& context, double d) const {
+        if (!(type_ & (1 << kNumberSchemaType)))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+        if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
+            return false;
+
+        if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
+            return false;
+        
+        if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
+            return false;
+        
+        return CreateParallelValidator(context);
+    }
+    
+    bool String(Context& context, const Ch* str, SizeType length, bool) const {
+        if (!(type_ & (1 << kStringSchemaType)))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+        if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
+            SizeType count;
+            if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
+                if (count < minLength_)
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
+                if (count > maxLength_)
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
+            }
+        }
+
+        if (pattern_ && !IsPatternMatch(pattern_, str, length))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
+
+        return CreateParallelValidator(context);
+    }
+
+    bool StartObject(Context& context) const { 
+        if (!(type_ & (1 << kObjectSchemaType)))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+        if (hasDependencies_ || hasRequired_) {
+            context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
+            std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
+        }
+
+        if (patternProperties_) { // pre-allocate schema array
+            SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
+            context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
+            context.patternPropertiesSchemaCount = 0;
+            std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
+        }
+
+        return CreateParallelValidator(context);
+    }
+    
+    bool Key(Context& context, const Ch* str, SizeType len, bool) const {
+        if (patternProperties_) {
+            context.patternPropertiesSchemaCount = 0;
+            for (SizeType i = 0; i < patternPropertyCount_; i++)
+                if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
+                    context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
+                    context.valueSchema = typeless_;
+                }
+        }
+
+        SizeType index;
+        if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
+            if (context.patternPropertiesSchemaCount > 0) {
+                context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
+                context.valueSchema = typeless_;
+                context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
+            }
+            else
+                context.valueSchema = properties_[index].schema;
+
+            if (context.propertyExist)
+                context.propertyExist[index] = true;
+
+            return true;
+        }
+
+        if (additionalPropertiesSchema_) {
+            if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
+                context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
+                context.valueSchema = typeless_;
+                context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
+            }
+            else
+                context.valueSchema = additionalPropertiesSchema_;
+            return true;
+        }
+        else if (additionalProperties_) {
+            context.valueSchema = typeless_;
+            return true;
+        }
+
+        if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
+
+        return true;
+    }
+
+    bool EndObject(Context& context, SizeType memberCount) const {
+        if (hasRequired_)
+            for (SizeType index = 0; index < propertyCount_; index++)
+                if (properties_[index].required)
+                    if (!context.propertyExist[index])
+                        RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
+
+        if (memberCount < minProperties_)
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
+
+        if (memberCount > maxProperties_)
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
+
+        if (hasDependencies_) {
+            for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
+                if (context.propertyExist[sourceIndex]) {
+                    if (properties_[sourceIndex].dependencies) {
+                        for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
+                            if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
+                                RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+                    }
+                    else if (properties_[sourceIndex].dependenciesSchema)
+                        if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
+                            RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+                }
+        }
+
+        return true;
+    }
+
+    bool StartArray(Context& context) const { 
+        if (!(type_ & (1 << kArraySchemaType)))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+        context.arrayElementIndex = 0;
+        context.inArray = true;
+
+        return CreateParallelValidator(context);
+    }
+
+    bool EndArray(Context& context, SizeType elementCount) const { 
+        context.inArray = false;
+        
+        if (elementCount < minItems_)
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
+        
+        if (elementCount > maxItems_)
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
+
+        return true;
+    }
+
+    // Generate functions for string literal according to Ch
+#define RAPIDJSON_STRING_(name, ...) \
+    static const ValueType& Get##name##String() {\
+        static const Ch s[] = { __VA_ARGS__, '\0' };\
+        static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
+        return v;\
+    }
+
+    RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
+    RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
+    RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
+    RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
+    RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
+    RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
+    RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
+    RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
+    RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
+    RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
+    RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
+    RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
+    RAPIDJSON_STRING_(Not, 'n', 'o', 't')
+    RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+    RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
+    RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
+    RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+    RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+    RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+    RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+    RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
+    RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
+    RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
+    RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
+    RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
+    RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
+    RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
+    RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
+    RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
+    RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
+    RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
+    RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
+    RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
+
+#undef RAPIDJSON_STRING_
+
+private:
+    enum SchemaValueType {
+        kNullSchemaType,
+        kBooleanSchemaType,
+        kObjectSchemaType,
+        kArraySchemaType,
+        kStringSchemaType,
+        kNumberSchemaType,
+        kIntegerSchemaType,
+        kTotalSchemaType
+    };
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+        typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+        typedef std::basic_regex<Ch> RegexType;
+#else
+        typedef char RegexType;
+#endif
+
+    struct SchemaArray {
+        SchemaArray() : schemas(), count() {}
+        ~SchemaArray() { AllocatorType::Free(schemas); }
+        const SchemaType** schemas;
+        SizeType begin; // begin index of context.validators
+        SizeType count;
+    };
+
+    template <typename V1, typename V2>
+    void AddUniqueElement(V1& a, const V2& v) {
+        for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
+            if (*itr == v)
+                return;
+        V1 c(v, *allocator_);
+        a.PushBack(c, *allocator_);
+    }
+
+    static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
+        typename ValueType::ConstMemberIterator itr = value.FindMember(name);
+        return itr != value.MemberEnd() ? &(itr->value) : 0;
+    }
+
+    static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
+        if (const ValueType* v = GetMember(value, name))
+            if (v->IsBool())
+                out = v->GetBool();
+    }
+
+    static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
+        if (const ValueType* v = GetMember(value, name))
+            if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
+                out = static_cast<SizeType>(v->GetUint64());
+    }
+
+    void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
+        if (const ValueType* v = GetMember(value, name)) {
+            if (v->IsArray() && v->Size() > 0) {
+                PointerType q = p.Append(name, allocator_);
+                out.count = v->Size();
+                out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
+                memset(out.schemas, 0, sizeof(Schema*)* out.count);
+                for (SizeType i = 0; i < out.count; i++)
+                    schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
+                out.begin = validatorCount_;
+                validatorCount_ += out.count;
+            }
+        }
+    }
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+    template <typename ValueType>
+    RegexType* CreatePattern(const ValueType& value) {
+        if (value.IsString()) {
+            RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
+            if (!r->IsValid()) {
+                r->~RegexType();
+                AllocatorType::Free(r);
+                r = 0;
+            }
+            return r;
+        }
+        return 0;
+    }
+
+    static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
+        GenericRegexSearch<RegexType> rs(*pattern);
+        return rs.Search(str);
+    }
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+    template <typename ValueType>
+    RegexType* CreatePattern(const ValueType& value) {
+        if (value.IsString())
+            try {
+                return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
+            }
+            catch (const std::regex_error&) {
+            }
+        return 0;
+    }
+
+    static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
+        std::match_results<const Ch*> r;
+        return std::regex_search(str, str + length, r, *pattern);
+    }
+#else
+    template <typename ValueType>
+    RegexType* CreatePattern(const ValueType&) { return 0; }
+
+    static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
+#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
+
+    void AddType(const ValueType& type) {
+        if      (type == GetNullString()   ) type_ |= 1 << kNullSchemaType;
+        else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
+        else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
+        else if (type == GetArrayString()  ) type_ |= 1 << kArraySchemaType;
+        else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
+        else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
+        else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
+    }
+
+    bool CreateParallelValidator(Context& context) const {
+        if (enum_ || context.arrayUniqueness)
+            context.hasher = context.factory.CreateHasher();
+
+        if (validatorCount_) {
+            RAPIDJSON_ASSERT(context.validators == 0);
+            context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
+            context.validatorCount = validatorCount_;
+
+            if (allOf_.schemas)
+                CreateSchemaValidators(context, allOf_);
+
+            if (anyOf_.schemas)
+                CreateSchemaValidators(context, anyOf_);
+            
+            if (oneOf_.schemas)
+                CreateSchemaValidators(context, oneOf_);
+            
+            if (not_)
+                context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
+            
+            if (hasSchemaDependencies_) {
+                for (SizeType i = 0; i < propertyCount_; i++)
+                    if (properties_[i].dependenciesSchema)
+                        context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
+            }
+        }
+
+        return true;
+    }
+
+    void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
+        for (SizeType i = 0; i < schemas.count; i++)
+            context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
+    }
+
+    // O(n)
+    bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
+        SizeType len = name.GetStringLength();
+        const Ch* str = name.GetString();
+        for (SizeType index = 0; index < propertyCount_; index++)
+            if (properties_[index].name.GetStringLength() == len && 
+                (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
+            {
+                *outIndex = index;
+                return true;
+            }
+        return false;
+    }
+
+    bool CheckInt(Context& context, int64_t i) const {
+        if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+        if (!minimum_.IsNull()) {
+            if (minimum_.IsInt64()) {
+                if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+            }
+            else if (minimum_.IsUint64()) {
+                RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
+            }
+            else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+                return false;
+        }
+
+        if (!maximum_.IsNull()) {
+            if (maximum_.IsInt64()) {
+                if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+            }
+            else if (maximum_.IsUint64()) { }
+                /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
+            else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+                return false;
+        }
+
+        if (!multipleOf_.IsNull()) {
+            if (multipleOf_.IsUint64()) {
+                if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+            }
+            else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+                return false;
+        }
+
+        return true;
+    }
+
+    bool CheckUint(Context& context, uint64_t i) const {
+        if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+        if (!minimum_.IsNull()) {
+            if (minimum_.IsUint64()) {
+                if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+            }
+            else if (minimum_.IsInt64())
+                /* do nothing */; // i >= 0 > minimum.Getint64()
+            else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+                return false;
+        }
+
+        if (!maximum_.IsNull()) {
+            if (maximum_.IsUint64()) {
+                if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+            }
+            else if (maximum_.IsInt64())
+                RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
+            else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+                return false;
+        }
+
+        if (!multipleOf_.IsNull()) {
+            if (multipleOf_.IsUint64()) {
+                if (i % multipleOf_.GetUint64() != 0)
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+            }
+            else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+                return false;
+        }
+
+        return true;
+    }
+
+    bool CheckDoubleMinimum(Context& context, double d) const {
+        if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+        return true;
+    }
+
+    bool CheckDoubleMaximum(Context& context, double d) const {
+        if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+        return true;
+    }
+
+    bool CheckDoubleMultipleOf(Context& context, double d) const {
+        double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
+        double q = std::floor(a / b);
+        double r = a - q * b;
+        if (r > 0.0)
+            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+        return true;
+    }
+
+    struct Property {
+        Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
+        ~Property() { AllocatorType::Free(dependencies); }
+        SValue name;
+        const SchemaType* schema;
+        const SchemaType* dependenciesSchema;
+        SizeType dependenciesValidatorIndex;
+        bool* dependencies;
+        bool required;
+    };
+
+    struct PatternProperty {
+        PatternProperty() : schema(), pattern() {}
+        ~PatternProperty() { 
+            if (pattern) {
+                pattern->~RegexType();
+                AllocatorType::Free(pattern);
+            }
+        }
+        const SchemaType* schema;
+        RegexType* pattern;
+    };
+
+    AllocatorType* allocator_;
+    const SchemaType* typeless_;
+    uint64_t* enum_;
+    SizeType enumCount_;
+    SchemaArray allOf_;
+    SchemaArray anyOf_;
+    SchemaArray oneOf_;
+    const SchemaType* not_;
+    unsigned type_; // bitmask of kSchemaType
+    SizeType validatorCount_;
+    SizeType notValidatorIndex_;
+
+    Property* properties_;
+    const SchemaType* additionalPropertiesSchema_;
+    PatternProperty* patternProperties_;
+    SizeType patternPropertyCount_;
+    SizeType propertyCount_;
+    SizeType minProperties_;
+    SizeType maxProperties_;
+    bool additionalProperties_;
+    bool hasDependencies_;
+    bool hasRequired_;
+    bool hasSchemaDependencies_;
+
+    const SchemaType* additionalItemsSchema_;
+    const SchemaType* itemsList_;
+    const SchemaType** itemsTuple_;
+    SizeType itemsTupleCount_;
+    SizeType minItems_;
+    SizeType maxItems_;
+    bool additionalItems_;
+    bool uniqueItems_;
+
+    RegexType* pattern_;
+    SizeType minLength_;
+    SizeType maxLength_;
+
+    SValue minimum_;
+    SValue maximum_;
+    SValue multipleOf_;
+    bool exclusiveMinimum_;
+    bool exclusiveMaximum_;
+};
+
+template<typename Stack, typename Ch>
+struct TokenHelper {
+    RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+        *documentStack.template Push<Ch>() = '/';
+        char buffer[21];
+        size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
+        for (size_t i = 0; i < length; i++)
+            *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
+    }
+};
+
+// Partial specialized version for char to prevent buffer copying.
+template <typename Stack>
+struct TokenHelper<Stack, char> {
+    RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+        if (sizeof(SizeType) == 4) {
+            char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
+            *buffer++ = '/';
+            const char* end = internal::u32toa(index, buffer);
+             documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
+        }
+        else {
+            char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
+            *buffer++ = '/';
+            const char* end = internal::u64toa(index, buffer);
+            documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
+        }
+    }
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// IGenericRemoteSchemaDocumentProvider
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider {
+public:
+    typedef typename SchemaDocumentType::Ch Ch;
+
+    virtual ~IGenericRemoteSchemaDocumentProvider() {}
+    virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaDocument
+
+//! JSON schema document.
+/*!
+    A JSON schema document is a compiled version of a JSON schema.
+    It is basically a tree of internal::Schema.
+
+    \note This is an immutable class (i.e. its instance cannot be modified after construction).
+    \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
+    \tparam Allocator Allocator type for allocating memory of this document.
+*/
+template <typename ValueT, typename Allocator = CrtAllocator>
+class GenericSchemaDocument {
+public:
+    typedef ValueT ValueType;
+    typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
+    typedef Allocator AllocatorType;
+    typedef typename ValueType::EncodingType EncodingType;
+    typedef typename EncodingType::Ch Ch;
+    typedef internal::Schema<GenericSchemaDocument> SchemaType;
+    typedef GenericPointer<ValueType, Allocator> PointerType;
+    friend class internal::Schema<GenericSchemaDocument>;
+    template <typename, typename, typename>
+    friend class GenericSchemaValidator;
+
+    //! Constructor.
+    /*!
+        Compile a JSON document into schema document.
+
+        \param document A JSON document as source.
+        \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
+        \param allocator An optional allocator instance for allocating memory. Can be null.
+    */
+    explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
+        remoteProvider_(remoteProvider),
+        allocator_(allocator),
+        ownAllocator_(),
+        root_(),
+        typeless_(),
+        schemaMap_(allocator, kInitialSchemaMapSize),
+        schemaRef_(allocator, kInitialSchemaRefSize)
+    {
+        if (!allocator_)
+            ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
+
+        typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
+        new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
+
+        // Generate root schema, it will call CreateSchema() to create sub-schemas,
+        // And call AddRefSchema() if there are $ref.
+        CreateSchemaRecursive(&root_, PointerType(), document, document);
+
+        // Resolve $ref
+        while (!schemaRef_.Empty()) {
+            SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
+            if (const SchemaType* s = GetSchema(refEntry->target)) {
+                if (refEntry->schema)
+                    *refEntry->schema = s;
+
+                // Create entry in map if not exist
+                if (!GetSchema(refEntry->source)) {
+                    new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
+                }
+            }
+            else if (refEntry->schema)
+                *refEntry->schema = typeless_;
+
+            refEntry->~SchemaRefEntry();
+        }
+
+        RAPIDJSON_ASSERT(root_ != 0);
+
+        schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Move constructor in C++11
+    GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
+        remoteProvider_(rhs.remoteProvider_),
+        allocator_(rhs.allocator_),
+        ownAllocator_(rhs.ownAllocator_),
+        root_(rhs.root_),
+        typeless_(rhs.typeless_),
+        schemaMap_(std::move(rhs.schemaMap_)),
+        schemaRef_(std::move(rhs.schemaRef_))
+    {
+        rhs.remoteProvider_ = 0;
+        rhs.allocator_ = 0;
+        rhs.ownAllocator_ = 0;
+        rhs.typeless_ = 0;
+    }
+#endif
+
+    //! Destructor
+    ~GenericSchemaDocument() {
+        while (!schemaMap_.Empty())
+            schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
+
+        if (typeless_) {
+            typeless_->~SchemaType();
+            Allocator::Free(typeless_);
+        }
+
+        RAPIDJSON_DELETE(ownAllocator_);
+    }
+
+    //! Get the root schema.
+    const SchemaType& GetRoot() const { return *root_; }
+
+private:
+    //! Prohibit copying
+    GenericSchemaDocument(const GenericSchemaDocument&);
+    //! Prohibit assignment
+    GenericSchemaDocument& operator=(const GenericSchemaDocument&);
+
+    struct SchemaRefEntry {
+        SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
+        PointerType source;
+        PointerType target;
+        const SchemaType** schema;
+    };
+
+    struct SchemaEntry {
+        SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
+        ~SchemaEntry() {
+            if (owned) {
+                schema->~SchemaType();
+                Allocator::Free(schema);
+            }
+        }
+        PointerType pointer;
+        SchemaType* schema;
+        bool owned;
+    };
+
+    void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+        if (schema)
+            *schema = typeless_;
+
+        if (v.GetType() == kObjectType) {
+            const SchemaType* s = GetSchema(pointer);
+            if (!s)
+                CreateSchema(schema, pointer, v, document);
+
+            for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
+                CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
+        }
+        else if (v.GetType() == kArrayType)
+            for (SizeType i = 0; i < v.Size(); i++)
+                CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
+    }
+
+    void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+        RAPIDJSON_ASSERT(pointer.IsValid());
+        if (v.IsObject()) {
+            if (!HandleRefSchema(pointer, schema, v, document)) {
+                SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
+                new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
+                if (schema)
+                    *schema = s;
+            }
+        }
+    }
+
+    bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
+        static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
+        static const ValueType kRefValue(kRefString, 4);
+
+        typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
+        if (itr == v.MemberEnd())
+            return false;
+
+        if (itr->value.IsString()) {
+            SizeType len = itr->value.GetStringLength();
+            if (len > 0) {
+                const Ch* s = itr->value.GetString();
+                SizeType i = 0;
+                while (i < len && s[i] != '#') // Find the first #
+                    i++;
+
+                if (i > 0) { // Remote reference, resolve immediately
+                    if (remoteProvider_) {
+                        if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
+                            PointerType pointer(&s[i], len - i, allocator_);
+                            if (pointer.IsValid()) {
+                                if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
+                                    if (schema)
+                                        *schema = sc;
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+                else if (s[i] == '#') { // Local reference, defer resolution
+                    PointerType pointer(&s[i], len - i, allocator_);
+                    if (pointer.IsValid()) {
+                        if (const ValueType* nv = pointer.Get(document))
+                            if (HandleRefSchema(source, schema, *nv, document))
+                                return true;
+
+                        new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    const SchemaType* GetSchema(const PointerType& pointer) const {
+        for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+            if (pointer == target->pointer)
+                return target->schema;
+        return 0;
+    }
+
+    PointerType GetPointer(const SchemaType* schema) const {
+        for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+            if (schema == target->schema)
+                return target->pointer;
+        return PointerType();
+    }
+
+    const SchemaType* GetTypeless() const { return typeless_; }
+
+    static const size_t kInitialSchemaMapSize = 64;
+    static const size_t kInitialSchemaRefSize = 64;
+
+    IRemoteSchemaDocumentProviderType* remoteProvider_;
+    Allocator *allocator_;
+    Allocator *ownAllocator_;
+    const SchemaType* root_;                //!< Root schema.
+    SchemaType* typeless_;
+    internal::Stack<Allocator> schemaMap_;  // Stores created Pointer -> Schemas
+    internal::Stack<Allocator> schemaRef_;  // Stores Pointer from $ref and schema which holds the $ref
+};
+
+//! GenericSchemaDocument using Value type.
+typedef GenericSchemaDocument<Value> SchemaDocument;
+//! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaValidator
+
+//! JSON Schema Validator.
+/*!
+    A SAX style JSON schema validator.
+    It uses a \c GenericSchemaDocument to validate SAX events.
+    It delegates the incoming SAX events to an output handler.
+    The default output handler does nothing.
+    It can be reused multiple times by calling \c Reset().
+
+    \tparam SchemaDocumentType Type of schema document.
+    \tparam OutputHandler Type of output handler. Default handler does nothing.
+    \tparam StateAllocator Allocator for storing the internal validation states.
+*/
+template <
+    typename SchemaDocumentType,
+    typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
+    typename StateAllocator = CrtAllocator>
+class GenericSchemaValidator :
+    public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>, 
+    public internal::ISchemaValidator
+{
+public:
+    typedef typename SchemaDocumentType::SchemaType SchemaType;
+    typedef typename SchemaDocumentType::PointerType PointerType;
+    typedef typename SchemaType::EncodingType EncodingType;
+    typedef typename EncodingType::Ch Ch;
+
+    //! Constructor without output handler.
+    /*!
+        \param schemaDocument The schema document to conform to.
+        \param allocator Optional allocator for storing internal validation states.
+        \param schemaStackCapacity Optional initial capacity of schema path stack.
+        \param documentStackCapacity Optional initial capacity of document path stack.
+    */
+    GenericSchemaValidator(
+        const SchemaDocumentType& schemaDocument,
+        StateAllocator* allocator = 0, 
+        size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+        size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+        :
+        schemaDocument_(&schemaDocument),
+        root_(schemaDocument.GetRoot()),
+        stateAllocator_(allocator),
+        ownStateAllocator_(0),
+        schemaStack_(allocator, schemaStackCapacity),
+        documentStack_(allocator, documentStackCapacity),
+        outputHandler_(0),
+        valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+        , depth_(0)
+#endif
+    {
+    }
+
+    //! Constructor with output handler.
+    /*!
+        \param schemaDocument The schema document to conform to.
+        \param allocator Optional allocator for storing internal validation states.
+        \param schemaStackCapacity Optional initial capacity of schema path stack.
+        \param documentStackCapacity Optional initial capacity of document path stack.
+    */
+    GenericSchemaValidator(
+        const SchemaDocumentType& schemaDocument,
+        OutputHandler& outputHandler,
+        StateAllocator* allocator = 0, 
+        size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+        size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+        :
+        schemaDocument_(&schemaDocument),
+        root_(schemaDocument.GetRoot()),
+        stateAllocator_(allocator),
+        ownStateAllocator_(0),
+        schemaStack_(allocator, schemaStackCapacity),
+        documentStack_(allocator, documentStackCapacity),
+        outputHandler_(&outputHandler),
+        valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+        , depth_(0)
+#endif
+    {
+    }
+
+    //! Destructor.
+    ~GenericSchemaValidator() {
+        Reset();
+        RAPIDJSON_DELETE(ownStateAllocator_);
+    }
+
+    //! Reset the internal states.
+    void Reset() {
+        while (!schemaStack_.Empty())
+            PopSchema();
+        documentStack_.Clear();
+        valid_ = true;
+    }
+
+    //! Checks whether the current state is valid.
+    // Implementation of ISchemaValidator
+    virtual bool IsValid() const { return valid_; }
+
+    //! Gets the JSON pointer pointed to the invalid schema.
+    PointerType GetInvalidSchemaPointer() const {
+        return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
+    }
+
+    //! Gets the keyword of invalid schema.
+    const Ch* GetInvalidSchemaKeyword() const {
+        return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
+    }
+
+    //! Gets the JSON pointer pointed to the invalid value.
+    PointerType GetInvalidDocumentPointer() const {
+        return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
+    }
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+    *documentStack_.template Push<Ch>() = '\0';\
+    documentStack_.template Pop<Ch>(1);\
+    internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
+RAPIDJSON_MULTILINEMACRO_END
+#else
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
+#endif
+
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
+    if (!valid_) return false; \
+    if (!BeginValue() || !CurrentSchema().method arg1) {\
+        RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
+        return valid_ = false;\
+    }
+
+#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
+    for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
+        if (context->hasher)\
+            static_cast<HasherType*>(context->hasher)->method arg2;\
+        if (context->validators)\
+            for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
+                static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
+        if (context->patternPropertiesValidators)\
+            for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
+                static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
+    }
+
+#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
+    return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
+
+#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
+    RAPIDJSON_SCHEMA_HANDLE_BEGIN_   (method, arg1);\
+    RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
+    RAPIDJSON_SCHEMA_HANDLE_END_     (method, arg2)
+
+    bool Null()             { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null,   (CurrentContext()   ), ( )); }
+    bool Bool(bool b)       { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool,   (CurrentContext(), b), (b)); }
+    bool Int(int i)         { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int,    (CurrentContext(), i), (i)); }
+    bool Uint(unsigned u)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint,   (CurrentContext(), u), (u)); }
+    bool Int64(int64_t i)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64,  (CurrentContext(), i), (i)); }
+    bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
+    bool Double(double d)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
+    bool RawNumber(const Ch* str, SizeType length, bool copy)
+                                    { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+    bool String(const Ch* str, SizeType length, bool copy)
+                                    { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+
+    bool StartObject() {
+        RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
+        RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
+        return valid_ = !outputHandler_ || outputHandler_->StartObject();
+    }
+    
+    bool Key(const Ch* str, SizeType len, bool copy) {
+        if (!valid_) return false;
+        AppendToken(str, len);
+        if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
+        RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
+        return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
+    }
+    
+    bool EndObject(SizeType memberCount) { 
+        if (!valid_) return false;
+        RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
+        if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
+        RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
+    }
+
+    bool StartArray() {
+        RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
+        RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
+        return valid_ = !outputHandler_ || outputHandler_->StartArray();
+    }
+    
+    bool EndArray(SizeType elementCount) {
+        if (!valid_) return false;
+        RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
+        if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
+        RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
+    }
+
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
+#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
+#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
+
+    // Implementation of ISchemaStateFactory<SchemaType>
+    virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
+        return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+        depth_ + 1,
+#endif
+        &GetStateAllocator());
+    }
+
+    virtual void DestroySchemaValidator(ISchemaValidator* validator) {
+        GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
+        v->~GenericSchemaValidator();
+        StateAllocator::Free(v);
+    }
+
+    virtual void* CreateHasher() {
+        return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
+    }
+
+    virtual uint64_t GetHashCode(void* hasher) {
+        return static_cast<HasherType*>(hasher)->GetHashCode();
+    }
+
+    virtual void DestroryHasher(void* hasher) {
+        HasherType* h = static_cast<HasherType*>(hasher);
+        h->~HasherType();
+        StateAllocator::Free(h);
+    }
+
+    virtual void* MallocState(size_t size) {
+        return GetStateAllocator().Malloc(size);
+    }
+
+    virtual void FreeState(void* p) {
+        StateAllocator::Free(p);
+    }
+
+private:
+    typedef typename SchemaType::Context Context;
+    typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
+    typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
+
+    GenericSchemaValidator( 
+        const SchemaDocumentType& schemaDocument,
+        const SchemaType& root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+        unsigned depth,
+#endif
+        StateAllocator* allocator = 0,
+        size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+        size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+        :
+        schemaDocument_(&schemaDocument),
+        root_(root),
+        stateAllocator_(allocator),
+        ownStateAllocator_(0),
+        schemaStack_(allocator, schemaStackCapacity),
+        documentStack_(allocator, documentStackCapacity),
+        outputHandler_(0),
+        valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+        , depth_(depth)
+#endif
+    {
+    }
+
+    StateAllocator& GetStateAllocator() {
+        if (!stateAllocator_)
+            stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
+        return *stateAllocator_;
+    }
+
+    bool BeginValue() {
+        if (schemaStack_.Empty())
+            PushSchema(root_);
+        else {
+            if (CurrentContext().inArray)
+                internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
+
+            if (!CurrentSchema().BeginValue(CurrentContext()))
+                return false;
+
+            SizeType count = CurrentContext().patternPropertiesSchemaCount;
+            const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
+            typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
+            bool valueUniqueness = CurrentContext().valueUniqueness;
+            RAPIDJSON_ASSERT(CurrentContext().valueSchema);
+            PushSchema(*CurrentContext().valueSchema);
+
+            if (count > 0) {
+                CurrentContext().objectPatternValidatorType = patternValidatorType;
+                ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
+                SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
+                va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
+                for (SizeType i = 0; i < count; i++)
+                    va[validatorCount++] = CreateSchemaValidator(*sa[i]);
+            }
+
+            CurrentContext().arrayUniqueness = valueUniqueness;
+        }
+        return true;
+    }
+
+    bool EndValue() {
+        if (!CurrentSchema().EndValue(CurrentContext()))
+            return false;
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+        GenericStringBuffer<EncodingType> sb;
+        schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
+
+        *documentStack_.template Push<Ch>() = '\0';
+        documentStack_.template Pop<Ch>(1);
+        internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
+#endif
+
+        uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
+        
+        PopSchema();
+
+        if (!schemaStack_.Empty()) {
+            Context& context = CurrentContext();
+            if (context.valueUniqueness) {
+                HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
+                if (!a)
+                    CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
+                for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
+                    if (itr->GetUint64() == h)
+                        RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
+                a->PushBack(h, GetStateAllocator());
+            }
+        }
+
+        // Remove the last token of document pointer
+        while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
+            ;
+
+        return true;
+    }
+
+    void AppendToken(const Ch* str, SizeType len) {
+        documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
+        *documentStack_.template PushUnsafe<Ch>() = '/';
+        for (SizeType i = 0; i < len; i++) {
+            if (str[i] == '~') {
+                *documentStack_.template PushUnsafe<Ch>() = '~';
+                *documentStack_.template PushUnsafe<Ch>() = '0';
+            }
+            else if (str[i] == '/') {
+                *documentStack_.template PushUnsafe<Ch>() = '~';
+                *documentStack_.template PushUnsafe<Ch>() = '1';
+            }
+            else
+                *documentStack_.template PushUnsafe<Ch>() = str[i];
+        }
+    }
+
+    RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
+    
+    RAPIDJSON_FORCEINLINE void PopSchema() {
+        Context* c = schemaStack_.template Pop<Context>(1);
+        if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
+            a->~HashCodeArray();
+            StateAllocator::Free(a);
+        }
+        c->~Context();
+    }
+
+    const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
+    Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
+    const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
+
+    static const size_t kDefaultSchemaStackCapacity = 1024;
+    static const size_t kDefaultDocumentStackCapacity = 256;
+    const SchemaDocumentType* schemaDocument_;
+    const SchemaType& root_;
+    StateAllocator* stateAllocator_;
+    StateAllocator* ownStateAllocator_;
+    internal::Stack<StateAllocator> schemaStack_;    //!< stack to store the current path of schema (BaseSchemaType *)
+    internal::Stack<StateAllocator> documentStack_;  //!< stack to store the current path of validating document (Ch)
+    OutputHandler* outputHandler_;
+    bool valid_;
+#if RAPIDJSON_SCHEMA_VERBOSE
+    unsigned depth_;
+#endif
+};
+
+typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidatingReader
+
+//! A helper class for parsing with validation.
+/*!
+    This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
+
+    \tparam parseFlags Combination of \ref ParseFlag.
+    \tparam InputStream Type of input stream, implementing Stream concept.
+    \tparam SourceEncoding Encoding of the input stream.
+    \tparam SchemaDocumentType Type of schema document.
+    \tparam StackAllocator Allocator type for stack.
+*/
+template <
+    unsigned parseFlags,
+    typename InputStream,
+    typename SourceEncoding,
+    typename SchemaDocumentType = SchemaDocument,
+    typename StackAllocator = CrtAllocator>
+class SchemaValidatingReader {
+public:
+    typedef typename SchemaDocumentType::PointerType PointerType;
+    typedef typename InputStream::Ch Ch;
+
+    //! Constructor
+    /*!
+        \param is Input stream.
+        \param sd Schema document.
+    */
+    SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
+
+    template <typename Handler>
+    bool operator()(Handler& handler) {
+        GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
+        GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
+        parseResult_ = reader.template Parse<parseFlags>(is_, validator);
+
+        isValid_ = validator.IsValid();
+        if (isValid_) {
+            invalidSchemaPointer_ = PointerType();
+            invalidSchemaKeyword_ = 0;
+            invalidDocumentPointer_ = PointerType();
+        }
+        else {
+            invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
+            invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
+            invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
+        }
+
+        return parseResult_;
+    }
+
+    const ParseResult& GetParseResult() const { return parseResult_; }
+    bool IsValid() const { return isValid_; }
+    const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
+    const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
+    const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
+
+private:
+    InputStream& is_;
+    const SchemaDocumentType& sd_;
+
+    ParseResult parseResult_;
+    PointerType invalidSchemaPointer_;
+    const Ch* invalidSchemaKeyword_;
+    PointerType invalidDocumentPointer_;
+    bool isValid_;
+};
+
+RAPIDJSON_NAMESPACE_END
+RAPIDJSON_DIAG_POP
+
+#endif // RAPIDJSON_SCHEMA_H_
diff --git a/src/lottie/rapidjson/stream.h b/src/lottie/rapidjson/stream.h
new file mode 100644 (file)
index 0000000..7f2643e
--- /dev/null
@@ -0,0 +1,223 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "rapidjson.h"
+
+#ifndef RAPIDJSON_STREAM_H_
+#define RAPIDJSON_STREAM_H_
+
+#include "encodings.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+//  Stream
+
+/*! \class rapidjson::Stream
+    \brief Concept for reading and writing characters.
+
+    For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
+
+    For write-only stream, only need to implement Put() and Flush().
+
+\code
+concept Stream {
+    typename Ch;    //!< Character type of the stream.
+
+    //! Read the current character from stream without moving the read cursor.
+    Ch Peek() const;
+
+    //! Read the current character from stream and moving the read cursor to next character.
+    Ch Take();
+
+    //! Get the current read cursor.
+    //! \return Number of characters read from start.
+    size_t Tell();
+
+    //! Begin writing operation at the current read pointer.
+    //! \return The begin writer pointer.
+    Ch* PutBegin();
+
+    //! Write a character.
+    void Put(Ch c);
+
+    //! Flush the buffer.
+    void Flush();
+
+    //! End the writing operation.
+    //! \param begin The begin write pointer returned by PutBegin().
+    //! \return Number of characters written.
+    size_t PutEnd(Ch* begin);
+}
+\endcode
+*/
+
+//! Provides additional information for stream.
+/*!
+    By using traits pattern, this type provides a default configuration for stream.
+    For custom stream, this type can be specialized for other configuration.
+    See TEST(Reader, CustomStringStream) in readertest.cpp for example.
+*/
+template<typename Stream>
+struct StreamTraits {
+    //! Whether to make local copy of stream for optimization during parsing.
+    /*!
+        By default, for safety, streams do not use local copy optimization.
+        Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
+    */
+    enum { copyOptimization = 0 };
+};
+
+//! Reserve n characters for writing to a stream.
+template<typename Stream>
+inline void PutReserve(Stream& stream, size_t count) {
+    (void)stream;
+    (void)count;
+}
+
+//! Write character to a stream, presuming buffer is reserved.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
+    stream.Put(c);
+}
+
+//! Put N copies of a character to a stream.
+template<typename Stream, typename Ch>
+inline void PutN(Stream& stream, Ch c, size_t n) {
+    PutReserve(stream, n);
+    for (size_t i = 0; i < n; i++)
+        PutUnsafe(stream, c);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericStreamWrapper
+
+//! A Stream Wrapper
+/*! \tThis string stream is a wrapper for any stream by just forwarding any
+    \treceived message to the origin stream.
+    \note implements Stream concept
+*/
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4702)  // unreachable code
+RAPIDJSON_DIAG_OFF(4512)  // assignment operator could not be generated
+#endif
+
+template <typename InputStream, typename Encoding = UTF8<> >
+class GenericStreamWrapper {
+public:
+    typedef typename Encoding::Ch Ch;
+    GenericStreamWrapper(InputStream& is): is_(is) {}
+
+    Ch Peek() const { return is_.Peek(); }
+    Ch Take() { return is_.Take(); }
+    size_t Tell() { return is_.Tell(); }
+    Ch* PutBegin() { return is_.PutBegin(); }
+    void Put(Ch ch) { is_.Put(ch); }
+    void Flush() { is_.Flush(); }
+    size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); }
+
+    // wrapper for MemoryStream
+    const Ch* Peek4() const { return is_.Peek4(); }
+
+    // wrapper for AutoUTFInputStream
+    UTFType GetType() const { return is_.GetType(); }
+    bool HasBOM() const { return is_.HasBOM(); }
+
+protected:
+    InputStream& is_;
+};
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+RAPIDJSON_DIAG_POP
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream
+
+//! Read-only string stream.
+/*! \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericStringStream {
+    typedef typename Encoding::Ch Ch;
+
+    GenericStringStream(const Ch *src) : src_(src), head_(src) {}
+
+    Ch Peek() const { return *src_; }
+    Ch Take() { return *src_++; }
+    size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+
+    Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+    void Put(Ch) { RAPIDJSON_ASSERT(false); }
+    void Flush() { RAPIDJSON_ASSERT(false); }
+    size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+    const Ch* src_;     //!< Current read position.
+    const Ch* head_;    //!< Original head of the string.
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericStringStream<Encoding> > {
+    enum { copyOptimization = 1 };
+};
+
+//! String stream with UTF8 encoding.
+typedef GenericStringStream<UTF8<> > StringStream;
+
+///////////////////////////////////////////////////////////////////////////////
+// InsituStringStream
+
+//! A read-write string stream.
+/*! This string stream is particularly designed for in-situ parsing.
+    \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericInsituStringStream {
+    typedef typename Encoding::Ch Ch;
+
+    GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
+
+    // Read
+    Ch Peek() { return *src_; }
+    Ch Take() { return *src_++; }
+    size_t Tell() { return static_cast<size_t>(src_ - head_); }
+
+    // Write
+    void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
+
+    Ch* PutBegin() { return dst_ = src_; }
+    size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
+    void Flush() {}
+
+    Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
+    void Pop(size_t count) { dst_ -= count; }
+
+    Ch* src_;
+    Ch* dst_;
+    Ch* head_;
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericInsituStringStream<Encoding> > {
+    enum { copyOptimization = 1 };
+};
+
+//! Insitu string stream with UTF8 encoding.
+typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STREAM_H_
diff --git a/src/lottie/rapidjson/stringbuffer.h b/src/lottie/rapidjson/stringbuffer.h
new file mode 100644 (file)
index 0000000..4e38b82
--- /dev/null
@@ -0,0 +1,121 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_STRINGBUFFER_H_
+#define RAPIDJSON_STRINGBUFFER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
+#include "internal/stack.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory output stream.
+/*!
+    \tparam Encoding Encoding of the stream.
+    \tparam Allocator type for allocating memory buffer.
+    \note implements Stream concept
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericStringBuffer {
+public:
+    typedef typename Encoding::Ch Ch;
+
+    GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
+    GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
+        if (&rhs != this)
+            stack_ = std::move(rhs.stack_);
+        return *this;
+    }
+#endif
+
+    void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+    void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
+    void Flush() {}
+
+    void Clear() { stack_.Clear(); }
+    void ShrinkToFit() {
+        // Push and pop a null terminator. This is safe.
+        *stack_.template Push<Ch>() = '\0';
+        stack_.ShrinkToFit();
+        stack_.template Pop<Ch>(1);
+    }
+
+    void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
+    Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+    Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
+    void Pop(size_t count) { stack_.template Pop<Ch>(count); }
+
+    const Ch* GetString() const {
+        // Push and pop a null terminator. This is safe.
+        *stack_.template Push<Ch>() = '\0';
+        stack_.template Pop<Ch>(1);
+
+        return stack_.template Bottom<Ch>();
+    }
+
+    //! Get the size of string in bytes in the string buffer.
+    size_t GetSize() const { return stack_.GetSize(); }
+
+    //! Get the length of string in Ch in the string buffer.
+    size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); }
+
+    static const size_t kDefaultCapacity = 256;
+    mutable internal::Stack<Allocator> stack_;
+
+private:
+    // Prohibit copy constructor & assignment operator.
+    GenericStringBuffer(const GenericStringBuffer&);
+    GenericStringBuffer& operator=(const GenericStringBuffer&);
+};
+
+//! String buffer with UTF8 encoding
+typedef GenericStringBuffer<UTF8<> > StringBuffer;
+
+template<typename Encoding, typename Allocator>
+inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
+    stream.Reserve(count);
+}
+
+template<typename Encoding, typename Allocator>
+inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
+    stream.PutUnsafe(c);
+}
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
+    std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_STRINGBUFFER_H_
diff --git a/src/lottie/rapidjson/writer.h b/src/lottie/rapidjson/writer.h
new file mode 100644 (file)
index 0000000..e610ebb
--- /dev/null
@@ -0,0 +1,711 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+// 
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed 
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_WRITER_H_
+#define RAPIDJSON_WRITER_H_
+
+#include "stream.h"
+#include "internal/meta.h"
+#include "internal/stack.h"
+#include "internal/strfunc.h"
+#include "internal/dtoa.h"
+#include "internal/itoa.h"
+#include "stringbuffer.h"
+#include <new>      // placement new
+
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#elif defined(RAPIDJSON_NEON)
+#include <arm_neon.h>
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteFlag
+
+/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS 
+    \ingroup RAPIDJSON_CONFIG
+    \brief User-defined kWriteDefaultFlags definition.
+
+    User can define this as any \c WriteFlag combinations.
+*/
+#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
+#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
+#endif
+
+//! Combination of writeFlags
+enum WriteFlag {
+    kWriteNoFlags = 0,              //!< No flags are set.
+    kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
+    kWriteNanAndInfFlag = 2,        //!< Allow writing of Infinity, -Infinity and NaN.
+    kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS  //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
+};
+
+//! JSON writer
+/*! Writer implements the concept Handler.
+    It generates JSON text by events to an output os.
+
+    User may programmatically calls the functions of a writer to generate JSON text.
+
+    On the other side, a writer can also be passed to objects that generates events, 
+
+    for example Reader::Parse() and Document::Accept().
+
+    \tparam OutputStream Type of output stream.
+    \tparam SourceEncoding Encoding of source string.
+    \tparam TargetEncoding Encoding of output stream.
+    \tparam StackAllocator Type of allocator for allocating memory of stack.
+    \note implements Handler concept
+*/
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class Writer {
+public:
+    typedef typename SourceEncoding::Ch Ch;
+
+    static const int kDefaultMaxDecimalPlaces = 324;
+
+    //! Constructor
+    /*! \param os Output stream.
+        \param stackAllocator User supplied allocator. If it is null, it will create a private one.
+        \param levelDepth Initial capacity of stack.
+    */
+    explicit
+    Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : 
+        os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
+
+    explicit
+    Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
+        os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    Writer(Writer&& rhs) :
+        os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) {
+        rhs.os_ = 0;
+    }
+#endif
+
+    //! Reset the writer with a new stream.
+    /*!
+        This function reset the writer with a new stream and default settings,
+        in order to make a Writer object reusable for output multiple JSONs.
+
+        \param os New output stream.
+        \code
+        Writer<OutputStream> writer(os1);
+        writer.StartObject();
+        // ...
+        writer.EndObject();
+
+        writer.Reset(os2);
+        writer.StartObject();
+        // ...
+        writer.EndObject();
+        \endcode
+    */
+    void Reset(OutputStream& os) {
+        os_ = &os;
+        hasRoot_ = false;
+        level_stack_.Clear();
+    }
+
+    //! Checks whether the output is a complete JSON.
+    /*!
+        A complete JSON has a complete root object or array.
+    */
+    bool IsComplete() const {
+        return hasRoot_ && level_stack_.Empty();
+    }
+
+    int GetMaxDecimalPlaces() const {
+        return maxDecimalPlaces_;
+    }
+
+    //! Sets the maximum number of decimal places for double output.
+    /*!
+        This setting truncates the output with specified number of decimal places.
+
+        For example, 
+
+        \code
+        writer.SetMaxDecimalPlaces(3);
+        writer.StartArray();
+        writer.Double(0.12345);                 // "0.123"
+        writer.Double(0.0001);                  // "0.0"
+        writer.Double(1.234567890123456e30);    // "1.234567890123456e30" (do not truncate significand for positive exponent)
+        writer.Double(1.23e-4);                 // "0.0"                  (do truncate significand for negative exponent)
+        writer.EndArray();
+        \endcode
+
+        The default setting does not truncate any decimal places. You can restore to this setting by calling
+        \code
+        writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
+        \endcode
+    */
+    void SetMaxDecimalPlaces(int maxDecimalPlaces) {
+        maxDecimalPlaces_ = maxDecimalPlaces;
+    }
+
+    /*!@name Implementation of Handler
+        \see Handler
+    */
+    //@{
+
+    bool Null()                 { Prefix(kNullType);   return EndValue(WriteNull()); }
+    bool Bool(bool b)           { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); }
+    bool Int(int i)             { Prefix(kNumberType); return EndValue(WriteInt(i)); }
+    bool Uint(unsigned u)       { Prefix(kNumberType); return EndValue(WriteUint(u)); }
+    bool Int64(int64_t i64)     { Prefix(kNumberType); return EndValue(WriteInt64(i64)); }
+    bool Uint64(uint64_t u64)   { Prefix(kNumberType); return EndValue(WriteUint64(u64)); }
+
+    //! Writes the given \c double value to the stream
+    /*!
+        \param d The value to be written.
+        \return Whether it is succeed.
+    */
+    bool Double(double d)       { Prefix(kNumberType); return EndValue(WriteDouble(d)); }
+
+    bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+        RAPIDJSON_ASSERT(str != 0);
+        (void)copy;
+        Prefix(kNumberType);
+        return EndValue(WriteString(str, length));
+    }
+
+    bool String(const Ch* str, SizeType length, bool copy = false) {
+        RAPIDJSON_ASSERT(str != 0);
+        (void)copy;
+        Prefix(kStringType);
+        return EndValue(WriteString(str, length));
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    bool String(const std::basic_string<Ch>& str) {
+        return String(str.data(), SizeType(str.size()));
+    }
+#endif
+
+    bool StartObject() {
+        Prefix(kObjectType);
+        new (level_stack_.template Push<Level>()) Level(false);
+        return WriteStartObject();
+    }
+
+    bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    bool Key(const std::basic_string<Ch>& str)
+    {
+      return Key(str.data(), SizeType(str.size()));
+    }
+#endif
+       
+    bool EndObject(SizeType memberCount = 0) {
+        (void)memberCount;
+        RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object
+        RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); // currently inside an Array, not Object
+        RAPIDJSON_ASSERT(0 == level_stack_.template Top<Level>()->valueCount % 2); // Object has a Key without a Value
+        level_stack_.template Pop<Level>(1);
+        return EndValue(WriteEndObject());
+    }
+
+    bool StartArray() {
+        Prefix(kArrayType);
+        new (level_stack_.template Push<Level>()) Level(true);
+        return WriteStartArray();
+    }
+
+    bool EndArray(SizeType elementCount = 0) {
+        (void)elementCount;
+        RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
+        RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
+        level_stack_.template Pop<Level>(1);
+        return EndValue(WriteEndArray());
+    }
+    //@}
+
+    /*! @name Convenience extensions */
+    //@{
+
+    //! Simpler but slower overload.
+    bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); }
+    bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); }
+    
+    //@}
+
+    //! Write a raw JSON value.
+    /*!
+        For user to write a stringified JSON as a value.
+
+        \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+        \param length Length of the json.
+        \param type Type of the root of json.
+    */
+    bool RawValue(const Ch* json, size_t length, Type type) {
+        RAPIDJSON_ASSERT(json != 0);
+        Prefix(type);
+        return EndValue(WriteRawValue(json, length));
+    }
+
+    //! Flush the output stream.
+    /*!
+        Allows the user to flush the output stream immediately.
+     */
+    void Flush() {
+        os_->Flush();
+    }
+
+protected:
+    //! Information for each nested level
+    struct Level {
+        Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
+        size_t valueCount;  //!< number of values in this level
+        bool inArray;       //!< true if in array, otherwise in object
+    };
+
+    static const size_t kDefaultLevelDepth = 32;
+
+    bool WriteNull()  {
+        PutReserve(*os_, 4);
+        PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
+    }
+
+    bool WriteBool(bool b)  {
+        if (b) {
+            PutReserve(*os_, 4);
+            PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
+        }
+        else {
+            PutReserve(*os_, 5);
+            PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
+        }
+        return true;
+    }
+
+    bool WriteInt(int i) {
+        char buffer[11];
+        const char* end = internal::i32toa(i, buffer);
+        PutReserve(*os_, static_cast<size_t>(end - buffer));
+        for (const char* p = buffer; p != end; ++p)
+            PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
+        return true;
+    }
+
+    bool WriteUint(unsigned u) {
+        char buffer[10];
+        const char* end = internal::u32toa(u, buffer);
+        PutReserve(*os_, static_cast<size_t>(end - buffer));
+        for (const char* p = buffer; p != end; ++p)
+            PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
+        return true;
+    }
+
+    bool WriteInt64(int64_t i64) {
+        char buffer[21];
+        const char* end = internal::i64toa(i64, buffer);
+        PutReserve(*os_, static_cast<size_t>(end - buffer));
+        for (const char* p = buffer; p != end; ++p)
+            PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
+        return true;
+    }
+
+    bool WriteUint64(uint64_t u64) {
+        char buffer[20];
+        char* end = internal::u64toa(u64, buffer);
+        PutReserve(*os_, static_cast<size_t>(end - buffer));
+        for (char* p = buffer; p != end; ++p)
+            PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
+        return true;
+    }
+
+    bool WriteDouble(double d) {
+        if (internal::Double(d).IsNanOrInf()) {
+            if (!(writeFlags & kWriteNanAndInfFlag))
+                return false;
+            if (internal::Double(d).IsNan()) {
+                PutReserve(*os_, 3);
+                PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
+                return true;
+            }
+            if (internal::Double(d).Sign()) {
+                PutReserve(*os_, 9);
+                PutUnsafe(*os_, '-');
+            }
+            else
+                PutReserve(*os_, 8);
+            PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
+            PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
+            return true;
+        }
+
+        char buffer[25];
+        char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+        PutReserve(*os_, static_cast<size_t>(end - buffer));
+        for (char* p = buffer; p != end; ++p)
+            PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
+        return true;
+    }
+
+    bool WriteString(const Ch* str, SizeType length)  {
+        static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+        static const char escape[256] = {
+#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+            //0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+            'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
+            'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
+              0,   0, '"',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 20
+            Z16, Z16,                                                                       // 30~4F
+              0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,'\\',   0,   0,   0, // 50
+            Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16                                // 60~FF
+#undef Z16
+        };
+
+        if (TargetEncoding::supportUnicode)
+            PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
+        else
+            PutReserve(*os_, 2 + length * 12);  // "\uxxxx\uyyyy..."
+
+        PutUnsafe(*os_, '\"');
+        GenericStringStream<SourceEncoding> is(str);
+        while (ScanWriteUnescapedString(is, length)) {
+            const Ch c = is.Peek();
+            if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
+                // Unicode escaping
+                unsigned codepoint;
+                if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
+                    return false;
+                PutUnsafe(*os_, '\\');
+                PutUnsafe(*os_, 'u');
+                if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
+                    PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
+                    PutUnsafe(*os_, hexDigits[(codepoint >>  8) & 15]);
+                    PutUnsafe(*os_, hexDigits[(codepoint >>  4) & 15]);
+                    PutUnsafe(*os_, hexDigits[(codepoint      ) & 15]);
+                }
+                else {
+                    RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
+                    // Surrogate pair
+                    unsigned s = codepoint - 0x010000;
+                    unsigned lead = (s >> 10) + 0xD800;
+                    unsigned trail = (s & 0x3FF) + 0xDC00;
+                    PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
+                    PutUnsafe(*os_, hexDigits[(lead >>  8) & 15]);
+                    PutUnsafe(*os_, hexDigits[(lead >>  4) & 15]);
+                    PutUnsafe(*os_, hexDigits[(lead      ) & 15]);
+                    PutUnsafe(*os_, '\\');
+                    PutUnsafe(*os_, 'u');
+                    PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
+                    PutUnsafe(*os_, hexDigits[(trail >>  8) & 15]);
+                    PutUnsafe(*os_, hexDigits[(trail >>  4) & 15]);
+                    PutUnsafe(*os_, hexDigits[(trail      ) & 15]);                    
+                }
+            }
+            else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)]))  {
+                is.Take();
+                PutUnsafe(*os_, '\\');
+                PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)]));
+                if (escape[static_cast<unsigned char>(c)] == 'u') {
+                    PutUnsafe(*os_, '0');
+                    PutUnsafe(*os_, '0');
+                    PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
+                    PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
+                }
+            }
+            else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? 
+                Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
+                Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
+                return false;
+        }
+        PutUnsafe(*os_, '\"');
+        return true;
+    }
+
+    bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
+        return RAPIDJSON_LIKELY(is.Tell() < length);
+    }
+
+    bool WriteStartObject() { os_->Put('{'); return true; }
+    bool WriteEndObject()   { os_->Put('}'); return true; }
+    bool WriteStartArray()  { os_->Put('['); return true; }
+    bool WriteEndArray()    { os_->Put(']'); return true; }
+
+    bool WriteRawValue(const Ch* json, size_t length) {
+        PutReserve(*os_, length);
+        for (size_t i = 0; i < length; i++) {
+            RAPIDJSON_ASSERT(json[i] != '\0');
+            PutUnsafe(*os_, json[i]);
+        }
+        return true;
+    }
+
+    void Prefix(Type type) {
+        (void)type;
+        if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
+            Level* level = level_stack_.template Top<Level>();
+            if (level->valueCount > 0) {
+                if (level->inArray) 
+                    os_->Put(','); // add comma if it is not the first element in array
+                else  // in object
+                    os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
+            }
+            if (!level->inArray && level->valueCount % 2 == 0)
+                RAPIDJSON_ASSERT(type == kStringType);  // if it's in object, then even number should be a name
+            level->valueCount++;
+        }
+        else {
+            RAPIDJSON_ASSERT(!hasRoot_);    // Should only has one and only one root.
+            hasRoot_ = true;
+        }
+    }
+
+    // Flush the value if it is the top level one.
+    bool EndValue(bool ret) {
+        if (RAPIDJSON_UNLIKELY(level_stack_.Empty()))   // end of json text
+            Flush();
+        return ret;
+    }
+
+    OutputStream* os_;
+    internal::Stack<StackAllocator> level_stack_;
+    int maxDecimalPlaces_;
+    bool hasRoot_;
+
+private:
+    // Prohibit copy constructor & assignment operator.
+    Writer(const Writer&);
+    Writer& operator=(const Writer&);
+};
+
+// Full specialization for StringStream to prevent memory copying
+
+template<>
+inline bool Writer<StringBuffer>::WriteInt(int i) {
+    char *buffer = os_->Push(11);
+    const char* end = internal::i32toa(i, buffer);
+    os_->Pop(static_cast<size_t>(11 - (end - buffer)));
+    return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
+    char *buffer = os_->Push(10);
+    const char* end = internal::u32toa(u, buffer);
+    os_->Pop(static_cast<size_t>(10 - (end - buffer)));
+    return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
+    char *buffer = os_->Push(21);
+    const char* end = internal::i64toa(i64, buffer);
+    os_->Pop(static_cast<size_t>(21 - (end - buffer)));
+    return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
+    char *buffer = os_->Push(20);
+    const char* end = internal::u64toa(u, buffer);
+    os_->Pop(static_cast<size_t>(20 - (end - buffer)));
+    return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteDouble(double d) {
+    if (internal::Double(d).IsNanOrInf()) {
+        // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
+        if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
+            return false;
+        if (internal::Double(d).IsNan()) {
+            PutReserve(*os_, 3);
+            PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
+            return true;
+        }
+        if (internal::Double(d).Sign()) {
+            PutReserve(*os_, 9);
+            PutUnsafe(*os_, '-');
+        }
+        else
+            PutReserve(*os_, 8);
+        PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
+        PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
+        return true;
+    }
+    
+    char *buffer = os_->Push(25);
+    char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+    os_->Pop(static_cast<size_t>(25 - (end - buffer)));
+    return true;
+}
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+template<>
+inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
+    if (length < 16)
+        return RAPIDJSON_LIKELY(is.Tell() < length);
+
+    if (!RAPIDJSON_LIKELY(is.Tell() < length))
+        return false;
+
+    const char* p = is.src_;
+    const char* end = is.head_ + length;
+    const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+    const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
+    if (nextAligned > end)
+        return true;
+
+    while (p != nextAligned)
+        if (*p < 0x20 || *p == '\"' || *p == '\\') {
+            is.src_ = p;
+            return RAPIDJSON_LIKELY(is.Tell() < length);
+        }
+        else
+            os_->PutUnsafe(*p++);
+
+    // The rest of string using SIMD
+    static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+    static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+    static const char space[16]  = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };
+    const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+    const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+    const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+    for (; p != endAligned; p += 16) {
+        const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+        const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+        const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+        const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F
+        const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+        unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+        if (RAPIDJSON_UNLIKELY(r != 0)) {   // some of characters is escaped
+            SizeType len;
+#ifdef _MSC_VER         // Find the index of first escaped
+            unsigned long offset;
+            _BitScanForward(&offset, r);
+            len = offset;
+#else
+            len = static_cast<SizeType>(__builtin_ffs(r) - 1);
+#endif
+            char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
+            for (size_t i = 0; i < len; i++)
+                q[i] = p[i];
+
+            p += len;
+            break;
+        }
+        _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
+    }
+
+    is.src_ = p;
+    return RAPIDJSON_LIKELY(is.Tell() < length);
+}
+#elif defined(RAPIDJSON_NEON)
+template<>
+inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
+    if (length < 16)
+        return RAPIDJSON_LIKELY(is.Tell() < length);
+
+    if (!RAPIDJSON_LIKELY(is.Tell() < length))
+        return false;
+
+    const char* p = is.src_;
+    const char* end = is.head_ + length;
+    const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+    const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
+    if (nextAligned > end)
+        return true;
+
+    while (p != nextAligned)
+        if (*p < 0x20 || *p == '\"' || *p == '\\') {
+            is.src_ = p;
+            return RAPIDJSON_LIKELY(is.Tell() < length);
+        }
+        else
+            os_->PutUnsafe(*p++);
+
+    // The rest of string using SIMD
+    const uint8x16_t s0 = vmovq_n_u8('"');
+    const uint8x16_t s1 = vmovq_n_u8('\\');
+    const uint8x16_t s2 = vmovq_n_u8('\b');
+    const uint8x16_t s3 = vmovq_n_u8(32);
+
+    for (; p != endAligned; p += 16) {
+        const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p));
+        uint8x16_t x = vceqq_u8(s, s0);
+        x = vorrq_u8(x, vceqq_u8(s, s1));
+        x = vorrq_u8(x, vceqq_u8(s, s2));
+        x = vorrq_u8(x, vcltq_u8(s, s3));
+
+        x = vrev64q_u8(x);                     // Rev in 64
+        uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
+        uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+
+        SizeType len = 0;
+        bool escaped = false;
+        if (low == 0) {
+            if (high != 0) {
+                unsigned lz = (unsigned)__builtin_clzll(high);
+                len = 8 + (lz >> 3);
+                escaped = true;
+            }
+        } else {
+            unsigned lz = (unsigned)__builtin_clzll(low);
+            len = lz >> 3;
+            escaped = true;
+        }
+        if (RAPIDJSON_UNLIKELY(escaped)) {   // some of characters is escaped
+            char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
+            for (size_t i = 0; i < len; i++)
+                q[i] = p[i];
+
+            p += len;
+            break;
+        }
+        vst1q_u8(reinterpret_cast<uint8_t *>(os_->PushUnsafe(16)), s);
+    }
+
+    is.src_ = p;
+    return RAPIDJSON_LIKELY(is.Tell() < length);
+}
+#endif // RAPIDJSON_NEON
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/src/meson.build b/src/meson.build
new file mode 100644 (file)
index 0000000..6f6bb37
--- /dev/null
@@ -0,0 +1,12 @@
+subdir('vector')
+subdir('lottie')
+
+library_deps  = [vector_dep]
+library_deps += lottie_dep
+
+lottie_player_lib = shared_library( 'lottie-player',
+                                    include_directories : inc,
+                                    version             : lottie_player_lib_version,
+                                    dependencies        : library_deps,
+                                    install             : true
+                                  )
diff --git a/src/vector/freetype/meson.build b/src/vector/freetype/meson.build
new file mode 100644 (file)
index 0000000..8bbff5c
--- /dev/null
@@ -0,0 +1,10 @@
+
+source_file  = files('v_ft_math.cpp')
+source_file += files('v_ft_raster.cpp')
+source_file += files('v_ft_stroker.cpp')
+
+
+freetype_dep = declare_dependency(
+                                    include_directories : include_directories('.'),
+                                    sources : source_file
+                                 )
\ No newline at end of file
diff --git a/src/vector/freetype/v_ft_math.cpp b/src/vector/freetype/v_ft_math.cpp
new file mode 100644 (file)
index 0000000..e1c8ad4
--- /dev/null
@@ -0,0 +1,528 @@
+/***************************************************************************/
+/*                                                                         */
+/*  fttrigon.c                                                             */
+/*                                                                         */
+/*    FreeType trigonometric functions (body).                             */
+/*                                                                         */
+/*  Copyright 2001-2005, 2012-2013 by                                      */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+#include <math.h>
+#include "v_ft_math.h"
+
+
+#define SW_FT_MSB( x )  ( 31 - __builtin_clz( x ) )
+
+#define SW_FT_PAD_FLOOR( x, n )  ( (x) & ~((n)-1) )
+#define SW_FT_PAD_ROUND( x, n )  SW_FT_PAD_FLOOR( (x) + ((n)/2), n )
+#define SW_FT_PAD_CEIL( x, n )   SW_FT_PAD_FLOOR( (x) + ((n)-1), n )
+
+
+#define SW_FT_BEGIN_STMNT  do {
+#define SW_FT_END_STMNT    } while ( 0 )
+/* transfer sign leaving a positive number */
+#define SW_FT_MOVE_SIGN( x, s ) \
+SW_FT_BEGIN_STMNT             \
+  if ( x < 0 )             \
+  {                        \
+    x = -x;                \
+    s = -s;                \
+  }                        \
+SW_FT_END_STMNT
+
+
+
+
+SW_FT_Long
+SW_FT_MulFix( SW_FT_Long  a,
+           SW_FT_Long  b )
+{
+  SW_FT_Int   s = 1;
+  SW_FT_Long  c;
+
+
+  SW_FT_MOVE_SIGN( a, s );
+  SW_FT_MOVE_SIGN( b, s );
+
+  c = (SW_FT_Long)( ( (SW_FT_Int64)a * b + 0x8000L ) >> 16 );
+
+  return ( s > 0 ) ? c : -c;
+}
+
+SW_FT_Long
+SW_FT_MulDiv( SW_FT_Long  a,
+           SW_FT_Long  b,
+           SW_FT_Long  c )
+{
+  SW_FT_Int   s = 1;
+  SW_FT_Long  d;
+
+
+  SW_FT_MOVE_SIGN( a, s );
+  SW_FT_MOVE_SIGN( b, s );
+  SW_FT_MOVE_SIGN( c, s );
+
+  d = (SW_FT_Long)( c > 0 ? ( (SW_FT_Int64)a * b + ( c >> 1 ) ) / c
+                       : 0x7FFFFFFFL );
+
+  return ( s > 0 ) ? d : -d;
+}
+
+SW_FT_Long
+SW_FT_DivFix( SW_FT_Long  a,
+           SW_FT_Long  b )
+{
+  SW_FT_Int   s = 1;
+  SW_FT_Long  q;
+
+
+  SW_FT_MOVE_SIGN( a, s );
+  SW_FT_MOVE_SIGN( b, s );
+
+  q = (SW_FT_Long)( b > 0 ? ( ( (SW_FT_UInt64)a << 16 ) + ( b >> 1 ) ) / b
+                       : 0x7FFFFFFFL );
+
+  return ( s < 0 ? -q : q );
+}
+
+
+/*************************************************************************/
+/*                                                                       */
+/* This is a fixed-point CORDIC implementation of trigonometric          */
+/* functions as well as transformations between Cartesian and polar      */
+/* coordinates.  The angles are represented as 16.16 fixed-point values  */
+/* in degrees, i.e., the angular resolution is 2^-16 degrees.  Note that */
+/* only vectors longer than 2^16*180/pi (or at least 22 bits) on a       */
+/* discrete Cartesian grid can have the same or better angular           */
+/* resolution.  Therefore, to maintain this precision, some functions    */
+/* require an interim upscaling of the vectors, whereas others operate   */
+/* with 24-bit long vectors directly.                                    */
+/*                                                                       */
+/*************************************************************************/
+
+  /* the Cordic shrink factor 0.858785336480436 * 2^32 */
+#define SW_FT_TRIG_SCALE      0xDBD95B16UL
+
+  /* the highest bit in overflow-safe vector components, */
+  /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30         */
+#define SW_FT_TRIG_SAFE_MSB   29
+
+  /* this table was generated for SW_FT_PI = 180L << 16, i.e. degrees */
+#define SW_FT_TRIG_MAX_ITERS  23
+
+  static const SW_FT_Fixed
+  ft_trig_arctan_table[] =
+  {
+    1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
+    14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
+    57L, 29L, 14L, 7L, 4L, 2L, 1L
+  };
+
+  /* multiply a given value by the CORDIC shrink factor */
+  static SW_FT_Fixed
+  ft_trig_downscale( SW_FT_Fixed  val )
+  {
+    SW_FT_Fixed  s;
+    SW_FT_Int64  v;
+
+
+    s   = val;
+    val = SW_FT_ABS( val );
+
+    v   = ( val * (SW_FT_Int64)SW_FT_TRIG_SCALE ) + 0x100000000UL;
+    val = (SW_FT_Fixed)( v >> 32 );
+
+    return ( s >= 0 ) ? val : -val;
+  }
+
+
+
+  /* undefined and never called for zero vector */
+  static SW_FT_Int
+  ft_trig_prenorm( SW_FT_Vector*  vec )
+  {
+    SW_FT_Pos  x, y;
+    SW_FT_Int  shift;
+
+
+    x = vec->x;
+    y = vec->y;
+
+    shift = SW_FT_MSB( SW_FT_ABS( x ) | SW_FT_ABS( y ) );
+
+    if ( shift <= SW_FT_TRIG_SAFE_MSB )
+    {
+      shift  = SW_FT_TRIG_SAFE_MSB - shift;
+      vec->x = (SW_FT_Pos)( (SW_FT_ULong)x << shift );
+      vec->y = (SW_FT_Pos)( (SW_FT_ULong)y << shift );
+    }
+    else
+    {
+      shift -= SW_FT_TRIG_SAFE_MSB;
+      vec->x = x >> shift;
+      vec->y = y >> shift;
+      shift  = -shift;
+    }
+
+    return shift;
+  }
+
+
+  static void
+  ft_trig_pseudo_rotate( SW_FT_Vector*  vec,
+                         SW_FT_Angle    theta )
+  {
+    SW_FT_Int           i;
+    SW_FT_Fixed         x, y, xtemp, b;
+    const SW_FT_Fixed  *arctanptr;
+
+
+    x = vec->x;
+    y = vec->y;
+
+    /* Rotate inside [-PI/4,PI/4] sector */
+    while ( theta < -SW_FT_ANGLE_PI4 )
+    {
+      xtemp  =  y;
+      y      = -x;
+      x      =  xtemp;
+      theta +=  SW_FT_ANGLE_PI2;
+    }
+
+    while ( theta > SW_FT_ANGLE_PI4 )
+    {
+      xtemp  = -y;
+      y      =  x;
+      x      =  xtemp;
+      theta -=  SW_FT_ANGLE_PI2;
+    }
+
+    arctanptr = ft_trig_arctan_table;
+
+    /* Pseudorotations, with right shifts */
+    for ( i = 1, b = 1; i < SW_FT_TRIG_MAX_ITERS; b <<= 1, i++ )
+    {
+      if ( theta < 0 )
+      {
+        xtemp  = x + ( ( y + b ) >> i );
+        y      = y - ( ( x + b ) >> i );
+        x      = xtemp;
+        theta += *arctanptr++;
+      }
+      else
+      {
+        xtemp  = x - ( ( y + b ) >> i );
+        y      = y + ( ( x + b ) >> i );
+        x      = xtemp;
+        theta -= *arctanptr++;
+      }
+    }
+
+    vec->x = x;
+    vec->y = y;
+  }
+
+
+  static void
+  ft_trig_pseudo_polarize( SW_FT_Vector*  vec )
+  {
+    SW_FT_Angle         theta;
+    SW_FT_Int           i;
+    SW_FT_Fixed         x, y, xtemp, b;
+    const SW_FT_Fixed  *arctanptr;
+
+
+    x = vec->x;
+    y = vec->y;
+
+    /* Get the vector into [-PI/4,PI/4] sector */
+    if ( y > x )
+    {
+      if ( y > -x )
+      {
+        theta =  SW_FT_ANGLE_PI2;
+        xtemp =  y;
+        y     = -x;
+        x     =  xtemp;
+      }
+      else
+      {
+        theta =  y > 0 ? SW_FT_ANGLE_PI : -SW_FT_ANGLE_PI;
+        x     = -x;
+        y     = -y;
+      }
+    }
+    else
+    {
+      if ( y < -x )
+      {
+        theta = -SW_FT_ANGLE_PI2;
+        xtemp = -y;
+        y     =  x;
+        x     =  xtemp;
+      }
+      else
+      {
+        theta = 0;
+      }
+    }
+
+    arctanptr = ft_trig_arctan_table;
+
+    /* Pseudorotations, with right shifts */
+    for ( i = 1, b = 1; i < SW_FT_TRIG_MAX_ITERS; b <<= 1, i++ )
+    {
+      if ( y > 0 )
+      {
+        xtemp  = x + ( ( y + b ) >> i );
+        y      = y - ( ( x + b ) >> i );
+        x      = xtemp;
+        theta += *arctanptr++;
+      }
+      else
+      {
+        xtemp  = x - ( ( y + b ) >> i );
+        y      = y + ( ( x + b ) >> i );
+        x      = xtemp;
+        theta -= *arctanptr++;
+      }
+    }
+
+    /* round theta */
+    if ( theta >= 0 )
+      theta = SW_FT_PAD_ROUND( theta, 32 );
+    else
+      theta = -SW_FT_PAD_ROUND( -theta, 32 );
+
+    vec->x = x;
+    vec->y = theta;
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  SW_FT_Fixed
+  SW_FT_Cos( SW_FT_Angle  angle )
+  {
+    SW_FT_Vector  v;
+
+
+    v.x = SW_FT_TRIG_SCALE >> 8;
+    v.y = 0;
+    ft_trig_pseudo_rotate( &v, angle );
+
+    return ( v.x + 0x80L ) >> 8;
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  SW_FT_Fixed
+  SW_FT_Sin( SW_FT_Angle  angle )
+  {
+    return SW_FT_Cos( SW_FT_ANGLE_PI2 - angle );
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  SW_FT_Fixed
+  SW_FT_Tan( SW_FT_Angle  angle )
+  {
+    SW_FT_Vector  v;
+
+
+    v.x = SW_FT_TRIG_SCALE >> 8;
+    v.y = 0;
+    ft_trig_pseudo_rotate( &v, angle );
+
+    return SW_FT_DivFix( v.y, v.x );
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  SW_FT_Angle
+  SW_FT_Atan2( SW_FT_Fixed  dx,
+            SW_FT_Fixed  dy )
+  {
+    SW_FT_Vector  v;
+
+
+    if ( dx == 0 && dy == 0 )
+      return 0;
+
+    v.x = dx;
+    v.y = dy;
+    ft_trig_prenorm( &v );
+    ft_trig_pseudo_polarize( &v );
+
+    return v.y;
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  void
+  SW_FT_Vector_Unit( SW_FT_Vector*  vec,
+                  SW_FT_Angle    angle )
+  {
+    vec->x = SW_FT_TRIG_SCALE >> 8;
+    vec->y = 0;
+    ft_trig_pseudo_rotate( vec, angle );
+    vec->x = ( vec->x + 0x80L ) >> 8;
+    vec->y = ( vec->y + 0x80L ) >> 8;
+  }
+
+
+  /* these macros return 0 for positive numbers,
+     and -1 for negative ones */
+#define SW_FT_SIGN_LONG( x )   ( (x) >> ( SW_FT_SIZEOF_LONG * 8 - 1 ) )
+#define SW_FT_SIGN_INT( x )    ( (x) >> ( SW_FT_SIZEOF_INT * 8 - 1 ) )
+#define SW_FT_SIGN_INT32( x )  ( (x) >> 31 )
+#define SW_FT_SIGN_INT16( x )  ( (x) >> 15 )
+
+
+  /* documentation is in fttrigon.h */
+
+  void
+  SW_FT_Vector_Rotate( SW_FT_Vector*  vec,
+                    SW_FT_Angle    angle )
+  {
+    SW_FT_Int     shift;
+    SW_FT_Vector  v;
+
+
+    v.x   = vec->x;
+    v.y   = vec->y;
+
+    if ( angle && ( v.x != 0 || v.y != 0 ) )
+    {
+      shift = ft_trig_prenorm( &v );
+      ft_trig_pseudo_rotate( &v, angle );
+      v.x = ft_trig_downscale( v.x );
+      v.y = ft_trig_downscale( v.y );
+
+      if ( shift > 0 )
+      {
+        SW_FT_Int32  half = (SW_FT_Int32)1L << ( shift - 1 );
+
+
+        vec->x = ( v.x + half + SW_FT_SIGN_LONG( v.x ) ) >> shift;
+        vec->y = ( v.y + half + SW_FT_SIGN_LONG( v.y ) ) >> shift;
+      }
+      else
+      {
+        shift  = -shift;
+        vec->x = (SW_FT_Pos)( (SW_FT_ULong)v.x << shift );
+        vec->y = (SW_FT_Pos)( (SW_FT_ULong)v.y << shift );
+      }
+    }
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  SW_FT_Fixed
+  SW_FT_Vector_Length( SW_FT_Vector*  vec )
+  {
+    SW_FT_Int     shift;
+    SW_FT_Vector  v;
+
+
+    v = *vec;
+
+    /* handle trivial cases */
+    if ( v.x == 0 )
+    {
+      return SW_FT_ABS( v.y );
+    }
+    else if ( v.y == 0 )
+    {
+      return SW_FT_ABS( v.x );
+    }
+
+    /* general case */
+    shift = ft_trig_prenorm( &v );
+    ft_trig_pseudo_polarize( &v );
+
+    v.x = ft_trig_downscale( v.x );
+
+    if ( shift > 0 )
+      return ( v.x + ( 1 << ( shift - 1 ) ) ) >> shift;
+
+    return (SW_FT_Fixed)( (SW_FT_UInt32)v.x << -shift );
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  void
+  SW_FT_Vector_Polarize( SW_FT_Vector*  vec,
+                      SW_FT_Fixed   *length,
+                      SW_FT_Angle   *angle )
+  {
+    SW_FT_Int     shift;
+    SW_FT_Vector  v;
+
+
+    v = *vec;
+
+    if ( v.x == 0 && v.y == 0 )
+      return;
+
+    shift = ft_trig_prenorm( &v );
+    ft_trig_pseudo_polarize( &v );
+
+    v.x = ft_trig_downscale( v.x );
+
+    *length = ( shift >= 0 ) ?                      ( v.x >>  shift )
+                             : (SW_FT_Fixed)( (SW_FT_UInt32)v.x << -shift );
+    *angle  = v.y;
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  void
+  SW_FT_Vector_From_Polar( SW_FT_Vector*  vec,
+                        SW_FT_Fixed    length,
+                        SW_FT_Angle    angle )
+  {
+    vec->x = length;
+    vec->y = 0;
+
+    SW_FT_Vector_Rotate( vec, angle );
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  SW_FT_Angle
+  SW_FT_Angle_Diff( SW_FT_Angle  angle1,
+                 SW_FT_Angle  angle2 )
+  {
+    SW_FT_Angle  delta = angle2 - angle1;
+
+
+    delta %= SW_FT_ANGLE_2PI;
+    if ( delta < 0 )
+      delta += SW_FT_ANGLE_2PI;
+
+    if ( delta > SW_FT_ANGLE_PI )
+      delta -= SW_FT_ANGLE_2PI;
+
+    return delta;
+  }
+
+
+/* END */
+
diff --git a/src/vector/freetype/v_ft_math.h b/src/vector/freetype/v_ft_math.h
new file mode 100644 (file)
index 0000000..b4611d8
--- /dev/null
@@ -0,0 +1,438 @@
+#ifndef V_FT_MATH_H
+#define V_FT_MATH_H
+
+/***************************************************************************/
+/*                                                                         */
+/*  fttrigon.h                                                             */
+/*                                                                         */
+/*    FreeType trigonometric functions (specification).                    */
+/*                                                                         */
+/*  Copyright 2001, 2003, 2005, 2007, 2013 by                              */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+#include "v_ft_types.h"
+
+
+/*************************************************************************/
+/*                                                                       */
+/* The min and max functions missing in C.  As usual, be careful not to  */
+/* write things like SW_FT_MIN( a++, b++ ) to avoid side effects.           */
+/*                                                                       */
+#define SW_FT_MIN( a, b )  ( (a) < (b) ? (a) : (b) )
+#define SW_FT_MAX( a, b )  ( (a) > (b) ? (a) : (b) )
+
+#define SW_FT_ABS( a )     ( (a) < 0 ? -(a) : (a) )
+
+/*
+ *  Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
+ *  algorithm.  We use alpha = 1, beta = 3/8, giving us results with a
+ *  largest error less than 7% compared to the exact value.
+ */
+#define SW_FT_HYPOT( x, y )                 \
+        ( x = SW_FT_ABS( x ),             \
+          y = SW_FT_ABS( y ),             \
+          x > y ? x + ( 3 * y >> 3 )   \
+                : y + ( 3 * x >> 3 ) )
+
+/*************************************************************************/
+/*                                                                       */
+/* <Function>                                                            */
+/*    SW_FT_MulFix                                                          */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A very simple function used to perform the computation             */
+/*    `(a*b)/0x10000' with maximum accuracy.  Most of the time this is   */
+/*    used to multiply a given value by a 16.16 fixed-point factor.      */
+/*                                                                       */
+/* <Input>                                                               */
+/*    a :: The first multiplier.                                         */
+/*    b :: The second multiplier.  Use a 16.16 factor here whenever      */
+/*         possible (see note below).                                    */
+/*                                                                       */
+/* <Return>                                                              */
+/*    The result of `(a*b)/0x10000'.                                     */
+/*                                                                       */
+/* <Note>                                                                */
+/*    This function has been optimized for the case where the absolute   */
+/*    value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */
+/*    As this happens mainly when scaling from notional units to         */
+/*    fractional pixels in FreeType, it resulted in noticeable speed     */
+/*    improvements between versions 2.x and 1.x.                         */
+/*                                                                       */
+/*    As a conclusion, always try to place a 16.16 factor as the         */
+/*    _second_ argument of this function; this can make a great          */
+/*    difference.                                                        */
+/*                                                                       */
+SW_FT_Long
+SW_FT_MulFix( SW_FT_Long  a,
+           SW_FT_Long  b );
+
+/*************************************************************************/
+/*                                                                       */
+/* <Function>                                                            */
+/*    SW_FT_MulDiv                                                          */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A very simple function used to perform the computation `(a*b)/c'   */
+/*    with maximum accuracy (it uses a 64-bit intermediate integer       */
+/*    whenever necessary).                                               */
+/*                                                                       */
+/*    This function isn't necessarily as fast as some processor specific */
+/*    operations, but is at least completely portable.                   */
+/*                                                                       */
+/* <Input>                                                               */
+/*    a :: The first multiplier.                                         */
+/*    b :: The second multiplier.                                        */
+/*    c :: The divisor.                                                  */
+/*                                                                       */
+/* <Return>                                                              */
+/*    The result of `(a*b)/c'.  This function never traps when trying to */
+/*    divide by zero; it simply returns `MaxInt' or `MinInt' depending   */
+/*    on the signs of `a' and `b'.                                       */
+/*                                                                       */
+SW_FT_Long
+SW_FT_MulDiv( SW_FT_Long  a,
+           SW_FT_Long  b,
+           SW_FT_Long  c );
+
+/*************************************************************************/
+/*                                                                       */
+/* <Function>                                                            */
+/*    SW_FT_DivFix                                                          */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A very simple function used to perform the computation             */
+/*    `(a*0x10000)/b' with maximum accuracy.  Most of the time, this is  */
+/*    used to divide a given value by a 16.16 fixed-point factor.        */
+/*                                                                       */
+/* <Input>                                                               */
+/*    a :: The numerator.                                                */
+/*    b :: The denominator.  Use a 16.16 factor here.                    */
+/*                                                                       */
+/* <Return>                                                              */
+/*    The result of `(a*0x10000)/b'.                                     */
+/*                                                                       */
+SW_FT_Long
+SW_FT_DivFix( SW_FT_Long  a,
+           SW_FT_Long  b );
+
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Section>                                                             */
+  /*   computations                                                        */
+  /*                                                                       */
+  /*************************************************************************/
+
+
+  /*************************************************************************
+   *
+   * @type:
+   *   SW_FT_Angle
+   *
+   * @description:
+   *   This type is used to model angle values in FreeType.  Note that the
+   *   angle is a 16.16 fixed-point value expressed in degrees.
+   *
+   */
+  typedef SW_FT_Fixed  SW_FT_Angle;
+
+
+  /*************************************************************************
+   *
+   * @macro:
+   *   SW_FT_ANGLE_PI
+   *
+   * @description:
+   *   The angle pi expressed in @SW_FT_Angle units.
+   *
+   */
+#define SW_FT_ANGLE_PI  ( 180L << 16 )
+
+
+  /*************************************************************************
+   *
+   * @macro:
+   *   SW_FT_ANGLE_2PI
+   *
+   * @description:
+   *   The angle 2*pi expressed in @SW_FT_Angle units.
+   *
+   */
+#define SW_FT_ANGLE_2PI  ( SW_FT_ANGLE_PI * 2 )
+
+
+  /*************************************************************************
+   *
+   * @macro:
+   *   SW_FT_ANGLE_PI2
+   *
+   * @description:
+   *   The angle pi/2 expressed in @SW_FT_Angle units.
+   *
+   */
+#define SW_FT_ANGLE_PI2  ( SW_FT_ANGLE_PI / 2 )
+
+
+  /*************************************************************************
+   *
+   * @macro:
+   *   SW_FT_ANGLE_PI4
+   *
+   * @description:
+   *   The angle pi/4 expressed in @SW_FT_Angle units.
+   *
+   */
+#define SW_FT_ANGLE_PI4  ( SW_FT_ANGLE_PI / 4 )
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Sin
+   *
+   * @description:
+   *   Return the sinus of a given angle in fixed-point format.
+   *
+   * @input:
+   *   angle ::
+   *     The input angle.
+   *
+   * @return:
+   *   The sinus value.
+   *
+   * @note:
+   *   If you need both the sinus and cosinus for a given angle, use the
+   *   function @SW_FT_Vector_Unit.
+   *
+   */
+  SW_FT_Fixed
+  SW_FT_Sin( SW_FT_Angle  angle );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Cos
+   *
+   * @description:
+   *   Return the cosinus of a given angle in fixed-point format.
+   *
+   * @input:
+   *   angle ::
+   *     The input angle.
+   *
+   * @return:
+   *   The cosinus value.
+   *
+   * @note:
+   *   If you need both the sinus and cosinus for a given angle, use the
+   *   function @SW_FT_Vector_Unit.
+   *
+   */
+  SW_FT_Fixed
+  SW_FT_Cos( SW_FT_Angle  angle );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Tan
+   *
+   * @description:
+   *   Return the tangent of a given angle in fixed-point format.
+   *
+   * @input:
+   *   angle ::
+   *     The input angle.
+   *
+   * @return:
+   *   The tangent value.
+   *
+   */
+  SW_FT_Fixed
+  SW_FT_Tan( SW_FT_Angle  angle );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Atan2
+   *
+   * @description:
+   *   Return the arc-tangent corresponding to a given vector (x,y) in
+   *   the 2d plane.
+   *
+   * @input:
+   *   x ::
+   *     The horizontal vector coordinate.
+   *
+   *   y ::
+   *     The vertical vector coordinate.
+   *
+   * @return:
+   *   The arc-tangent value (i.e. angle).
+   *
+   */
+  SW_FT_Angle
+  SW_FT_Atan2( SW_FT_Fixed  x,
+            SW_FT_Fixed  y );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Angle_Diff
+   *
+   * @description:
+   *   Return the difference between two angles.  The result is always
+   *   constrained to the ]-PI..PI] interval.
+   *
+   * @input:
+   *   angle1 ::
+   *     First angle.
+   *
+   *   angle2 ::
+   *     Second angle.
+   *
+   * @return:
+   *   Constrained value of `value2-value1'.
+   *
+   */
+  SW_FT_Angle
+  SW_FT_Angle_Diff( SW_FT_Angle  angle1,
+                 SW_FT_Angle  angle2 );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Vector_Unit
+   *
+   * @description:
+   *   Return the unit vector corresponding to a given angle.  After the
+   *   call, the value of `vec.x' will be `sin(angle)', and the value of
+   *   `vec.y' will be `cos(angle)'.
+   *
+   *   This function is useful to retrieve both the sinus and cosinus of a
+   *   given angle quickly.
+   *
+   * @output:
+   *   vec ::
+   *     The address of target vector.
+   *
+   * @input:
+   *   angle ::
+   *     The input angle.
+   *
+   */
+  void
+  SW_FT_Vector_Unit( SW_FT_Vector*  vec,
+                  SW_FT_Angle    angle );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Vector_Rotate
+   *
+   * @description:
+   *   Rotate a vector by a given angle.
+   *
+   * @inout:
+   *   vec ::
+   *     The address of target vector.
+   *
+   * @input:
+   *   angle ::
+   *     The input angle.
+   *
+   */
+  void
+  SW_FT_Vector_Rotate( SW_FT_Vector*  vec,
+                    SW_FT_Angle    angle );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Vector_Length
+   *
+   * @description:
+   *   Return the length of a given vector.
+   *
+   * @input:
+   *   vec ::
+   *     The address of target vector.
+   *
+   * @return:
+   *   The vector length, expressed in the same units that the original
+   *   vector coordinates.
+   *
+   */
+  SW_FT_Fixed
+  SW_FT_Vector_Length( SW_FT_Vector*  vec );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Vector_Polarize
+   *
+   * @description:
+   *   Compute both the length and angle of a given vector.
+   *
+   * @input:
+   *   vec ::
+   *     The address of source vector.
+   *
+   * @output:
+   *   length ::
+   *     The vector length.
+   *
+   *   angle ::
+   *     The vector angle.
+   *
+   */
+  void
+  SW_FT_Vector_Polarize( SW_FT_Vector*  vec,
+                      SW_FT_Fixed   *length,
+                      SW_FT_Angle   *angle );
+
+
+  /*************************************************************************
+   *
+   * @function:
+   *   SW_FT_Vector_From_Polar
+   *
+   * @description:
+   *   Compute vector coordinates from a length and angle.
+   *
+   * @output:
+   *   vec ::
+   *     The address of source vector.
+   *
+   * @input:
+   *   length ::
+   *     The vector length.
+   *
+   *   angle ::
+   *     The vector angle.
+   *
+   */
+  void
+  SW_FT_Vector_From_Polar( SW_FT_Vector*  vec,
+                        SW_FT_Fixed    length,
+                        SW_FT_Angle    angle );
+
+
+#endif // V_FT_MATH_H
diff --git a/src/vector/freetype/v_ft_raster.cpp b/src/vector/freetype/v_ft_raster.cpp
new file mode 100644 (file)
index 0000000..6059f0c
--- /dev/null
@@ -0,0 +1,1674 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ftgrays.c                                                              */
+/*                                                                         */
+/*    A new `perfect' anti-aliasing renderer (body).                       */
+/*                                                                         */
+/*  Copyright 2000-2003, 2005-2014 by                                      */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* This is a new anti-aliasing scan-converter for FreeType 2.  The       */
+  /* algorithm used here is _very_ different from the one in the standard  */
+  /* `ftraster' module.  Actually, `ftgrays' computes the _exact_          */
+  /* coverage of the outline on each pixel cell.                           */
+  /*                                                                       */
+  /* It is based on ideas that I initially found in Raph Levien's          */
+  /* excellent LibArt graphics library (see http://www.levien.com/libart   */
+  /* for more information, though the web pages do not tell anything       */
+  /* about the renderer; you'll have to dive into the source code to       */
+  /* understand how it works).                                             */
+  /*                                                                       */
+  /* Note, however, that this is a _very_ different implementation         */
+  /* compared to Raph's.  Coverage information is stored in a very         */
+  /* different way, and I don't use sorted vector paths.  Also, it doesn't */
+  /* use floating point values.                                            */
+  /*                                                                       */
+  /* This renderer has the following advantages:                           */
+  /*                                                                       */
+  /* - It doesn't need an intermediate bitmap.  Instead, one can supply a  */
+  /*   callback function that will be called by the renderer to draw gray  */
+  /*   spans on any target surface.  You can thus do direct composition on */
+  /*   any kind of bitmap, provided that you give the renderer the right   */
+  /*   callback.                                                           */
+  /*                                                                       */
+  /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on   */
+  /*   each pixel cell.                                                    */
+  /*                                                                       */
+  /* - It performs a single pass on the outline (the `standard' FT2        */
+  /*   renderer makes two passes).                                         */
+  /*                                                                       */
+  /* - It can easily be modified to render to _any_ number of gray levels  */
+  /*   cheaply.                                                            */
+  /*                                                                       */
+  /* - For small (< 20) pixel sizes, it is faster than the standard        */
+  /*   renderer.                                                           */
+  /*                                                                       */
+  /*************************************************************************/
+
+
+#include "v_ft_raster.h"
+#include "v_ft_math.h"
+
+  /* Auxiliary macros for token concatenation. */
+#define SW_FT_ERR_XCAT( x, y )  x ## y
+#define SW_FT_ERR_CAT( x, y )   SW_FT_ERR_XCAT( x, y )
+
+#define SW_FT_BEGIN_STMNT  do {
+#define SW_FT_END_STMNT    } while ( 0 )
+
+
+#include <stddef.h>
+#include <string.h>
+#include <setjmp.h>
+#include <limits.h>
+#define SW_FT_UINT_MAX   UINT_MAX
+#define SW_FT_INT_MAX    INT_MAX
+#define SW_FT_ULONG_MAX  ULONG_MAX
+#define SW_FT_CHAR_BIT   CHAR_BIT
+
+#define ft_memset   memset
+
+#define ft_setjmp   setjmp
+#define ft_longjmp  longjmp
+#define ft_jmp_buf  jmp_buf
+
+typedef ptrdiff_t  SW_FT_PtrDist;
+
+
+#define ErrRaster_Invalid_Mode      -2
+#define ErrRaster_Invalid_Outline   -1
+#define ErrRaster_Invalid_Argument  -3
+#define ErrRaster_Memory_Overflow   -4
+
+#define SW_FT_BEGIN_HEADER
+#define SW_FT_END_HEADER
+
+
+  /* This macro is used to indicate that a function parameter is unused. */
+  /* Its purpose is simply to reduce compiler warnings.  Note also that  */
+  /* simply defining it as `(void)x' doesn't avoid warnings with certain */
+  /* ANSI compilers (e.g. LCC).                                          */
+#define SW_FT_UNUSED( x )  (x) = (x)
+
+
+#define SW_FT_THROW( e )   SW_FT_ERR_CAT( ErrRaster_, e )
+
+  /* The size in bytes of the render pool used by the scan-line converter  */
+  /* to do all of its work.                                                */
+#define SW_FT_RENDER_POOL_SIZE  16384L
+
+typedef int
+(*SW_FT_Outline_MoveToFunc)( const SW_FT_Vector*  to,
+                          void*             user );
+
+#define SW_FT_Outline_MoveTo_Func  SW_FT_Outline_MoveToFunc
+
+typedef int
+(*SW_FT_Outline_LineToFunc)( const SW_FT_Vector*  to,
+                          void*             user );
+
+#define SW_FT_Outline_LineTo_Func  SW_FT_Outline_LineToFunc
+
+
+typedef int
+(*SW_FT_Outline_ConicToFunc)( const SW_FT_Vector*  control,
+                           const SW_FT_Vector*  to,
+                           void*             user );
+
+#define SW_FT_Outline_ConicTo_Func  SW_FT_Outline_ConicToFunc
+
+typedef int
+(*SW_FT_Outline_CubicToFunc)( const SW_FT_Vector*  control1,
+                           const SW_FT_Vector*  control2,
+                           const SW_FT_Vector*  to,
+                           void*             user );
+
+#define SW_FT_Outline_CubicTo_Func  SW_FT_Outline_CubicToFunc
+
+typedef struct  SW_FT_Outline_Funcs_
+{
+  SW_FT_Outline_MoveToFunc   move_to;
+  SW_FT_Outline_LineToFunc   line_to;
+  SW_FT_Outline_ConicToFunc  conic_to;
+  SW_FT_Outline_CubicToFunc  cubic_to;
+
+  int                        shift;
+  SW_FT_Pos                  delta;
+
+} SW_FT_Outline_Funcs;
+
+
+
+#define SW_FT_DEFINE_OUTLINE_FUNCS( class_,               \
+                                 move_to_, line_to_,   \
+                                 conic_to_, cubic_to_, \
+                                 shift_, delta_ )      \
+          static const SW_FT_Outline_Funcs class_ =       \
+          {                                            \
+            move_to_,                                  \
+            line_to_,                                  \
+            conic_to_,                                 \
+            cubic_to_,                                 \
+            shift_,                                    \
+            delta_                                     \
+         };
+
+#define SW_FT_DEFINE_RASTER_FUNCS( class_,                        \
+                                raster_new_, raster_reset_,       \
+                                raster_render_,                   \
+                                raster_done_ )                    \
+          const SW_FT_Raster_Funcs class_ =                       \
+          {                                                       \
+            raster_new_,                                          \
+            raster_reset_,                                        \
+            raster_render_,                                       \
+            raster_done_                                          \
+         };
+
+
+#ifndef SW_FT_MEM_SET
+#define SW_FT_MEM_SET( d, s, c )  ft_memset( d, s, c )
+#endif
+
+#ifndef SW_FT_MEM_ZERO
+#define SW_FT_MEM_ZERO( dest, count )  SW_FT_MEM_SET( dest, 0, count )
+#endif
+
+  /* as usual, for the speed hungry :-) */
+
+#undef RAS_ARG
+#undef RAS_ARG_
+#undef RAS_VAR
+#undef RAS_VAR_
+
+#ifndef SW_FT_STATIC_RASTER
+
+#define RAS_ARG   gray_PWorker  worker
+#define RAS_ARG_  gray_PWorker  worker,
+
+#define RAS_VAR   worker
+#define RAS_VAR_  worker,
+
+#else /* SW_FT_STATIC_RASTER */
+
+#define RAS_ARG   /* empty */
+#define RAS_ARG_  /* empty */
+#define RAS_VAR   /* empty */
+#define RAS_VAR_  /* empty */
+
+#endif /* SW_FT_STATIC_RASTER */
+
+
+  /* must be at least 6 bits! */
+#define PIXEL_BITS  8
+
+#undef FLOOR
+#undef CEILING
+#undef TRUNC
+#undef SCALED
+
+#define ONE_PIXEL       ( 1L << PIXEL_BITS )
+#define PIXEL_MASK      ( -1L << PIXEL_BITS )
+#define TRUNC( x )      ( (TCoord)( (x) >> PIXEL_BITS ) )
+#define SUBPIXELS( x )  ( (TPos)(x) << PIXEL_BITS )
+#define FLOOR( x )      ( (x) & -ONE_PIXEL )
+#define CEILING( x )    ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
+#define ROUND( x )      ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
+
+#if PIXEL_BITS >= 6
+#define UPSCALE( x )    ( (x) << ( PIXEL_BITS - 6 ) )
+#define DOWNSCALE( x )  ( (x) >> ( PIXEL_BITS - 6 ) )
+#else
+#define UPSCALE( x )    ( (x) >> ( 6 - PIXEL_BITS ) )
+#define DOWNSCALE( x )  ( (x) << ( 6 - PIXEL_BITS ) )
+#endif
+
+
+  /* Compute `dividend / divisor' and return both its quotient and     */
+  /* remainder, cast to a specific type.  This macro also ensures that */
+  /* the remainder is always positive.                                 */
+#define SW_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \
+  SW_FT_BEGIN_STMNT                                                   \
+    (quotient)  = (type)( (dividend) / (divisor) );                \
+    (remainder) = (type)( (dividend) % (divisor) );                \
+    if ( (remainder) < 0 )                                         \
+    {                                                              \
+      (quotient)--;                                                \
+      (remainder) += (type)(divisor);                              \
+    }                                                              \
+  SW_FT_END_STMNT
+
+#ifdef  __arm__
+  /* Work around a bug specific to GCC which make the compiler fail to */
+  /* optimize a division and modulo operation on the same parameters   */
+  /* into a single call to `__aeabi_idivmod'.  See                     */
+  /*                                                                   */
+  /*  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721                */
+#undef SW_FT_DIV_MOD
+#define SW_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \
+  SW_FT_BEGIN_STMNT                                                   \
+    (quotient)  = (type)( (dividend) / (divisor) );                \
+    (remainder) = (type)( (dividend) - (quotient) * (divisor) );   \
+    if ( (remainder) < 0 )                                         \
+    {                                                              \
+      (quotient)--;                                                \
+      (remainder) += (type)(divisor);                              \
+    }                                                              \
+  SW_FT_END_STMNT
+#endif /* __arm__ */
+
+  /* These macros speed up repetitive divisions by replacing them */
+  /* with multiplications and right shifts.                       */
+#define SW_FT_UDIVPREP( b )                                       \
+  long  b ## _r = (long)( SW_FT_ULONG_MAX >> PIXEL_BITS ) / ( b )
+#define SW_FT_UDIV( a, b )                                        \
+  ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >>   \
+    ( sizeof( long ) * SW_FT_CHAR_BIT - PIXEL_BITS ) )
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /*   TYPE DEFINITIONS                                                    */
+  /*                                                                       */
+
+  /* don't change the following types to SW_FT_Int or SW_FT_Pos, since we might */
+  /* need to define them to "float" or "double" when experimenting with   */
+  /* new algorithms                                                       */
+
+  typedef long  TCoord;   /* integer scanline/pixel coordinate */
+  typedef long  TPos;     /* sub-pixel coordinate              */
+
+  /* determine the type used to store cell areas.  This normally takes at */
+  /* least PIXEL_BITS*2 + 1 bits.  On 16-bit systems, we need to use      */
+  /* `long' instead of `int', otherwise bad things happen                 */
+
+#if PIXEL_BITS <= 7
+
+  typedef int  TArea;
+
+#else /* PIXEL_BITS >= 8 */
+
+  /* approximately determine the size of integers using an ANSI-C header */
+#if SW_FT_UINT_MAX == 0xFFFFU
+  typedef long  TArea;
+#else
+  typedef int   TArea;
+#endif
+
+#endif /* PIXEL_BITS >= 8 */
+
+
+  /* maximum number of gray spans in a call to the span callback */
+#define SW_FT_MAX_GRAY_SPANS  256
+
+
+  typedef struct TCell_*  PCell;
+
+  typedef struct  TCell_
+  {
+    TPos    x;     /* same with gray_TWorker.ex    */
+    TCoord  cover; /* same with gray_TWorker.cover */
+    TArea   area;
+    PCell   next;
+
+  } TCell;
+
+
+#if defined( _MSC_VER )      /* Visual C++ (and Intel C++) */
+  /* We disable the warning `structure was padded due to   */
+  /* __declspec(align())' in order to compile cleanly with */
+  /* the maximum level of warnings.                        */
+#pragma warning( push )
+#pragma warning( disable : 4324 )
+#endif /* _MSC_VER */
+
+  typedef struct  gray_TWorker_
+  {
+    TCoord  ex, ey;
+    TPos    min_ex, max_ex;
+    TPos    min_ey, max_ey;
+    TPos    count_ex, count_ey;
+
+    TArea   area;
+    TCoord  cover;
+    int     invalid;
+
+    PCell       cells;
+    SW_FT_PtrDist  max_cells;
+    SW_FT_PtrDist  num_cells;
+
+    TPos    x,  y;
+
+
+    SW_FT_Vector   bez_stack[32 * 3 + 1];
+    int         lev_stack[32];
+
+    SW_FT_Outline  outline;
+    SW_FT_BBox     clip_box;
+
+    SW_FT_Span     gray_spans[SW_FT_MAX_GRAY_SPANS];
+    int         num_gray_spans;
+
+    SW_FT_Raster_Span_Func  render_span;
+    void*                render_span_data;
+
+    int  band_size;
+    int  band_shoot;
+
+    ft_jmp_buf  jump_buffer;
+
+    void*       buffer;
+    long        buffer_size;
+
+    PCell*     ycells;
+    TPos       ycount;
+
+  } gray_TWorker, *gray_PWorker;
+
+#if defined( _MSC_VER )
+#pragma warning( pop )
+#endif
+
+
+#ifndef SW_FT_STATIC_RASTER
+#define ras  (*worker)
+#else
+  static gray_TWorker  ras;
+#endif
+
+
+  typedef struct gray_TRaster_
+  {
+    void*         memory;
+
+  } gray_TRaster, *gray_PRaster;
+
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Initialize the cells table.                                           */
+  /*                                                                       */
+  static void
+  gray_init_cells( RAS_ARG_ void*  buffer,
+                   long            byte_size )
+  {
+    ras.buffer      = buffer;
+    ras.buffer_size = byte_size;
+
+    ras.ycells      = (PCell*) buffer;
+    ras.cells       = NULL;
+    ras.max_cells   = 0;
+    ras.num_cells   = 0;
+    ras.area        = 0;
+    ras.cover       = 0;
+    ras.invalid     = 1;
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Compute the outline bounding box.                                     */
+  /*                                                                       */
+  static void
+  gray_compute_cbox( RAS_ARG )
+  {
+    SW_FT_Outline*  outline = &ras.outline;
+    SW_FT_Vector*   vec     = outline->points;
+    SW_FT_Vector*   limit   = vec + outline->n_points;
+
+
+    if ( outline->n_points <= 0 )
+    {
+      ras.min_ex = ras.max_ex = 0;
+      ras.min_ey = ras.max_ey = 0;
+      return;
+    }
+
+    ras.min_ex = ras.max_ex = vec->x;
+    ras.min_ey = ras.max_ey = vec->y;
+
+    vec++;
+
+    for ( ; vec < limit; vec++ )
+    {
+      TPos  x = vec->x;
+      TPos  y = vec->y;
+
+
+      if ( x < ras.min_ex ) ras.min_ex = x;
+      if ( x > ras.max_ex ) ras.max_ex = x;
+      if ( y < ras.min_ey ) ras.min_ey = y;
+      if ( y > ras.max_ey ) ras.max_ey = y;
+    }
+
+    /* truncate the bounding box to integer pixels */
+    ras.min_ex = ras.min_ex >> 6;
+    ras.min_ey = ras.min_ey >> 6;
+    ras.max_ex = ( ras.max_ex + 63 ) >> 6;
+    ras.max_ey = ( ras.max_ey + 63 ) >> 6;
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Record the current cell in the table.                                 */
+  /*                                                                       */
+  static PCell
+  gray_find_cell( RAS_ARG )
+  {
+    PCell  *pcell, cell;
+    TPos    x = ras.ex;
+
+
+    if ( x > ras.count_ex )
+      x = ras.count_ex;
+
+    pcell = &ras.ycells[ras.ey];
+    for (;;)
+    {
+      cell = *pcell;
+      if ( cell == NULL || cell->x > x )
+        break;
+
+      if ( cell->x == x )
+        goto Exit;
+
+      pcell = &cell->next;
+    }
+
+    if ( ras.num_cells >= ras.max_cells )
+      ft_longjmp( ras.jump_buffer, 1 );
+
+    cell        = ras.cells + ras.num_cells++;
+    cell->x     = x;
+    cell->area  = 0;
+    cell->cover = 0;
+
+    cell->next  = *pcell;
+    *pcell      = cell;
+
+  Exit:
+    return cell;
+  }
+
+
+  static void
+  gray_record_cell( RAS_ARG )
+  {
+    if ( ras.area | ras.cover )
+    {
+      PCell  cell = gray_find_cell( RAS_VAR );
+
+
+      cell->area  += ras.area;
+      cell->cover += ras.cover;
+    }
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Set the current cell to a new position.                               */
+  /*                                                                       */
+  static void
+  gray_set_cell( RAS_ARG_ TCoord  ex,
+                          TCoord  ey )
+  {
+    /* Move the cell pointer to a new position.  We set the `invalid'      */
+    /* flag to indicate that the cell isn't part of those we're interested */
+    /* in during the render phase.  This means that:                       */
+    /*                                                                     */
+    /* . the new vertical position must be within min_ey..max_ey-1.        */
+    /* . the new horizontal position must be strictly less than max_ex     */
+    /*                                                                     */
+    /* Note that if a cell is to the left of the clipping region, it is    */
+    /* actually set to the (min_ex-1) horizontal position.                 */
+
+    /* All cells that are on the left of the clipping region go to the */
+    /* min_ex - 1 horizontal position.                                 */
+    ey -= ras.min_ey;
+
+    if ( ex > ras.max_ex )
+      ex = ras.max_ex;
+
+    ex -= ras.min_ex;
+    if ( ex < 0 )
+      ex = -1;
+
+    /* are we moving to a different cell ? */
+    if ( ex != ras.ex || ey != ras.ey )
+    {
+      /* record the current one if it is valid */
+      if ( !ras.invalid )
+        gray_record_cell( RAS_VAR );
+
+      ras.area  = 0;
+      ras.cover = 0;
+      ras.ex    = ex;
+      ras.ey    = ey;
+    }
+
+    ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey ||
+                              ex >= ras.count_ex           );
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Start a new contour at a given cell.                                  */
+  /*                                                                       */
+  static void
+  gray_start_cell( RAS_ARG_ TCoord  ex,
+                            TCoord  ey )
+  {
+    if ( ex > ras.max_ex )
+      ex = (TCoord)( ras.max_ex );
+
+    if ( ex < ras.min_ex )
+      ex = (TCoord)( ras.min_ex - 1 );
+
+    ras.area    = 0;
+    ras.cover   = 0;
+    ras.ex      = ex - ras.min_ex;
+    ras.ey      = ey - ras.min_ey;
+    ras.invalid = 0;
+
+    gray_set_cell( RAS_VAR_ ex, ey );
+  }
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Render a straight line across multiple cells in any direction.        */
+  /*                                                                       */
+  static void
+  gray_render_line( RAS_ARG_ TPos  to_x,
+                             TPos  to_y )
+  {
+    TPos    dx, dy, fx1, fy1, fx2, fy2;
+    TCoord  ex1, ex2, ey1, ey2;
+
+
+    ex1 = TRUNC( ras.x );
+    ex2 = TRUNC( to_x );
+    ey1 = TRUNC( ras.y );
+    ey2 = TRUNC( to_y );
+
+    /* perform vertical clipping */
+    if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) ||
+         ( ey1 <  ras.min_ey && ey2 <  ras.min_ey ) )
+      goto End;
+
+    dx = to_x - ras.x;
+    dy = to_y - ras.y;
+
+    fx1 = ras.x - SUBPIXELS( ex1 );
+    fy1 = ras.y - SUBPIXELS( ey1 );
+
+    if ( ex1 == ex2 && ey1 == ey2 )       /* inside one cell */
+      ;
+    else if ( dy == 0 ) /* ex1 != ex2 */  /* any horizontal line */
+    {
+      ex1 = ex2;
+      gray_set_cell( RAS_VAR_ ex1, ey1 );
+    }
+    else if ( dx == 0 )
+    {
+      if ( dy > 0 )                       /* vertical line up */
+        do
+        {
+          fy2 = ONE_PIXEL;
+          ras.cover += ( fy2 - fy1 );
+          ras.area  += ( fy2 - fy1 ) * fx1 * 2;
+          fy1 = 0;
+          ey1++;
+          gray_set_cell( RAS_VAR_ ex1, ey1 );
+        } while ( ey1 != ey2 );
+      else                                /* vertical line down */
+        do
+        {
+          fy2 = 0;
+          ras.cover += ( fy2 - fy1 );
+          ras.area  += ( fy2 - fy1 ) * fx1 * 2;
+          fy1 = ONE_PIXEL;
+          ey1--;
+          gray_set_cell( RAS_VAR_ ex1, ey1 );
+        } while ( ey1 != ey2 );
+    }
+    else                                  /* any other line */
+    {
+      TArea  prod = dx * fy1 - dy * fx1;
+      SW_FT_UDIVPREP( dx );
+      SW_FT_UDIVPREP( dy );
+
+
+      /* The fundamental value `prod' determines which side and the  */
+      /* exact coordinate where the line exits current cell.  It is  */
+      /* also easily updated when moving from one cell to the next.  */
+      do
+      {
+        if      ( prod                                   <= 0 &&
+                  prod - dx * ONE_PIXEL                  >  0 ) /* left */
+        {
+          fx2 = 0;
+          fy2 = (TPos)SW_FT_UDIV( -prod, -dx );
+          prod -= dy * ONE_PIXEL;
+          ras.cover += ( fy2 - fy1 );
+          ras.area  += ( fy2 - fy1 ) * ( fx1 + fx2 );
+          fx1 = ONE_PIXEL;
+          fy1 = fy2;
+          ex1--;
+        }
+        else if ( prod - dx * ONE_PIXEL                  <= 0 &&
+                  prod - dx * ONE_PIXEL + dy * ONE_PIXEL >  0 ) /* up */
+        {
+          prod -= dx * ONE_PIXEL;
+          fx2 = (TPos)SW_FT_UDIV( -prod, dy );
+          fy2 = ONE_PIXEL;
+          ras.cover += ( fy2 - fy1 );
+          ras.area  += ( fy2 - fy1 ) * ( fx1 + fx2 );
+          fx1 = fx2;
+          fy1 = 0;
+          ey1++;
+        }
+        else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 &&
+                  prod                  + dy * ONE_PIXEL >= 0 ) /* right */
+        {
+          prod += dy * ONE_PIXEL;
+          fx2 = ONE_PIXEL;
+          fy2 = (TPos)SW_FT_UDIV( prod, dx );
+          ras.cover += ( fy2 - fy1 );
+          ras.area  += ( fy2 - fy1 ) * ( fx1 + fx2 );
+          fx1 = 0;
+          fy1 = fy2;
+          ex1++;
+        }
+        else /* ( prod                  + dy * ONE_PIXEL <  0 &&
+                  prod                                   >  0 )    down */
+        {
+          fx2 = (TPos)SW_FT_UDIV( prod, -dy );
+          fy2 = 0;
+          prod += dx * ONE_PIXEL;
+          ras.cover += ( fy2 - fy1 );
+          ras.area  += ( fy2 - fy1 ) * ( fx1 + fx2 );
+          fx1 = fx2;
+          fy1 = ONE_PIXEL;
+          ey1--;
+        }
+
+        gray_set_cell( RAS_VAR_ ex1, ey1 );
+      } while ( ex1 != ex2 || ey1 != ey2 );
+    }
+
+    fx2 = to_x - SUBPIXELS( ex2 );
+    fy2 = to_y - SUBPIXELS( ey2 );
+
+    ras.cover += ( fy2 - fy1 );
+    ras.area  += ( fy2 - fy1 ) * ( fx1 + fx2 );
+
+  End:
+    ras.x       = to_x;
+    ras.y       = to_y;
+  }
+
+  static void
+  gray_split_conic( SW_FT_Vector*  base )
+  {
+    TPos  a, b;
+
+
+    base[4].x = base[2].x;
+    b = base[1].x;
+    a = base[3].x = ( base[2].x + b ) / 2;
+    b = base[1].x = ( base[0].x + b ) / 2;
+    base[2].x = ( a + b ) / 2;
+
+    base[4].y = base[2].y;
+    b = base[1].y;
+    a = base[3].y = ( base[2].y + b ) / 2;
+    b = base[1].y = ( base[0].y + b ) / 2;
+    base[2].y = ( a + b ) / 2;
+  }
+
+
+  static void
+  gray_render_conic( RAS_ARG_ const SW_FT_Vector*  control,
+                              const SW_FT_Vector*  to )
+  {
+    TPos        dx, dy;
+    TPos        min, max, y;
+    int         top, level;
+    int*        levels;
+    SW_FT_Vector*  arc;
+
+
+    levels = ras.lev_stack;
+
+    arc      = ras.bez_stack;
+    arc[0].x = UPSCALE( to->x );
+    arc[0].y = UPSCALE( to->y );
+    arc[1].x = UPSCALE( control->x );
+    arc[1].y = UPSCALE( control->y );
+    arc[2].x = ras.x;
+    arc[2].y = ras.y;
+    top      = 0;
+
+    dx = SW_FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x );
+    dy = SW_FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y );
+    if ( dx < dy )
+      dx = dy;
+
+    if ( dx < ONE_PIXEL / 4 )
+      goto Draw;
+
+    /* short-cut the arc that crosses the current band */
+    min = max = arc[0].y;
+
+    y = arc[1].y;
+    if ( y < min ) min = y;
+    if ( y > max ) max = y;
+
+    y = arc[2].y;
+    if ( y < min ) min = y;
+    if ( y > max ) max = y;
+
+    if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
+      goto Draw;
+
+    level = 0;
+    do
+    {
+      dx >>= 2;
+      level++;
+    } while ( dx > ONE_PIXEL / 4 );
+
+    levels[0] = level;
+
+    do
+    {
+      level = levels[top];
+      if ( level > 0 )
+      {
+        gray_split_conic( arc );
+        arc += 2;
+        top++;
+        levels[top] = levels[top - 1] = level - 1;
+        continue;
+      }
+
+    Draw:
+      gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
+      top--;
+      arc -= 2;
+
+    } while ( top >= 0 );
+  }
+
+
+  static void
+  gray_split_cubic( SW_FT_Vector*  base )
+  {
+    TPos  a, b, c, d;
+
+
+    base[6].x = base[3].x;
+    c = base[1].x;
+    d = base[2].x;
+    base[1].x = a = ( base[0].x + c ) / 2;
+    base[5].x = b = ( base[3].x + d ) / 2;
+    c = ( c + d ) / 2;
+    base[2].x = a = ( a + c ) / 2;
+    base[4].x = b = ( b + c ) / 2;
+    base[3].x = ( a + b ) / 2;
+
+    base[6].y = base[3].y;
+    c = base[1].y;
+    d = base[2].y;
+    base[1].y = a = ( base[0].y + c ) / 2;
+    base[5].y = b = ( base[3].y + d ) / 2;
+    c = ( c + d ) / 2;
+    base[2].y = a = ( a + c ) / 2;
+    base[4].y = b = ( b + c ) / 2;
+    base[3].y = ( a + b ) / 2;
+  }
+
+
+  static void
+  gray_render_cubic( RAS_ARG_ const SW_FT_Vector*  control1,
+                              const SW_FT_Vector*  control2,
+                              const SW_FT_Vector*  to )
+  {
+    SW_FT_Vector*  arc;
+    TPos        min, max, y;
+
+
+    arc      = ras.bez_stack;
+    arc[0].x = UPSCALE( to->x );
+    arc[0].y = UPSCALE( to->y );
+    arc[1].x = UPSCALE( control2->x );
+    arc[1].y = UPSCALE( control2->y );
+    arc[2].x = UPSCALE( control1->x );
+    arc[2].y = UPSCALE( control1->y );
+    arc[3].x = ras.x;
+    arc[3].y = ras.y;
+
+    /* Short-cut the arc that crosses the current band. */
+    min = max = arc[0].y;
+
+    y = arc[1].y;
+    if ( y < min )
+      min = y;
+    if ( y > max )
+      max = y;
+
+    y = arc[2].y;
+    if ( y < min )
+      min = y;
+    if ( y > max )
+      max = y;
+
+    y = arc[3].y;
+    if ( y < min )
+      min = y;
+    if ( y > max )
+      max = y;
+
+    if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
+      goto Draw;
+
+    for (;;)
+    {
+      /* Decide whether to split or draw. See `Rapid Termination          */
+      /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
+      /* F. Hain, at                                                      */
+      /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
+
+      {
+        TPos  dx, dy, dx_, dy_;
+        TPos  dx1, dy1, dx2, dy2;
+        TPos  L, s, s_limit;
+
+
+        /* dx and dy are x and y components of the P0-P3 chord vector. */
+        dx = dx_ = arc[3].x - arc[0].x;
+        dy = dy_ = arc[3].y - arc[0].y;
+
+        L = SW_FT_HYPOT( dx_, dy_ );
+
+        /* Avoid possible arithmetic overflow below by splitting. */
+        if ( L > 32767 )
+          goto Split;
+
+        /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
+        s_limit = L * (TPos)( ONE_PIXEL / 6 );
+
+        /* s is L * the perpendicular distance from P1 to the line P0-P3. */
+        dx1 = arc[1].x - arc[0].x;
+        dy1 = arc[1].y - arc[0].y;
+        s = SW_FT_ABS( dy * dx1 - dx * dy1 );
+
+        if ( s > s_limit )
+          goto Split;
+
+        /* s is L * the perpendicular distance from P2 to the line P0-P3. */
+        dx2 = arc[2].x - arc[0].x;
+        dy2 = arc[2].y - arc[0].y;
+        s = SW_FT_ABS( dy * dx2 - dx * dy2 );
+
+        if ( s > s_limit )
+          goto Split;
+
+        /* Split super curvy segments where the off points are so far
+           from the chord that the angles P0-P1-P3 or P0-P2-P3 become
+           acute as detected by appropriate dot products. */
+        if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 ||
+             dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 )
+          goto Split;
+
+        /* No reason to split. */
+        goto Draw;
+      }
+
+    Split:
+      gray_split_cubic( arc );
+      arc += 3;
+      continue;
+
+    Draw:
+      gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
+
+      if ( arc == ras.bez_stack )
+        return;
+
+      arc -= 3;
+    }
+  }
+
+
+  static int
+  gray_move_to( const SW_FT_Vector*  to,
+                gray_PWorker      worker )
+  {
+    TPos  x, y;
+
+
+    /* record current cell, if any */
+    if ( !ras.invalid )
+      gray_record_cell( RAS_VAR );
+
+    /* start to a new position */
+    x = UPSCALE( to->x );
+    y = UPSCALE( to->y );
+
+    gray_start_cell( RAS_VAR_ TRUNC( x ), TRUNC( y ) );
+
+    worker->x = x;
+    worker->y = y;
+    return 0;
+  }
+
+
+  static int
+  gray_line_to( const SW_FT_Vector*  to,
+                gray_PWorker      worker )
+  {
+    gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) );
+    return 0;
+  }
+
+
+  static int
+  gray_conic_to( const SW_FT_Vector*  control,
+                 const SW_FT_Vector*  to,
+                 gray_PWorker      worker )
+  {
+    gray_render_conic( RAS_VAR_ control, to );
+    return 0;
+  }
+
+
+  static int
+  gray_cubic_to( const SW_FT_Vector*  control1,
+                 const SW_FT_Vector*  control2,
+                 const SW_FT_Vector*  to,
+                 gray_PWorker      worker )
+  {
+    gray_render_cubic( RAS_VAR_ control1, control2, to );
+    return 0;
+  }
+
+
+  static void
+  gray_hline( RAS_ARG_ TCoord  x,
+                       TCoord  y,
+                       TPos    area,
+                       TCoord  acount )
+  {
+    int  coverage;
+
+
+    /* compute the coverage line's coverage, depending on the    */
+    /* outline fill rule                                         */
+    /*                                                           */
+    /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
+    /*                                                           */
+    coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
+                                                    /* use range 0..256 */
+    if ( coverage < 0 )
+      coverage = -coverage;
+
+    if ( ras.outline.flags & SW_FT_OUTLINE_EVEN_ODD_FILL )
+    {
+      coverage &= 511;
+
+      if ( coverage > 256 )
+        coverage = 512 - coverage;
+      else if ( coverage == 256 )
+        coverage = 255;
+    }
+    else
+    {
+      /* normal non-zero winding rule */
+      if ( coverage >= 256 )
+        coverage = 255;
+    }
+
+    y += (TCoord)ras.min_ey;
+    x += (TCoord)ras.min_ex;
+
+    /* SW_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
+    if ( x >= 32767 )
+      x = 32767;
+
+    /* SW_FT_Span.y is an integer, so limit our coordinates appropriately */
+    if ( y >= SW_FT_INT_MAX )
+      y = SW_FT_INT_MAX;
+
+    if ( coverage )
+    {
+      SW_FT_Span*  span;
+      int       count;
+
+
+      /* see whether we can add this span to the current list */
+      count = ras.num_gray_spans;
+      span  = ras.gray_spans + count - 1;
+      if ( count > 0                          &&
+           span->y == y                    &&
+           (int)span->x + span->len == (int)x &&
+           span->coverage == coverage         )
+      {
+        span->len = (unsigned short)( span->len + acount );
+        return;
+      }
+
+      if ( count >= SW_FT_MAX_GRAY_SPANS )
+      {
+        if ( ras.render_span && count > 0 )
+          ras.render_span(count, ras.gray_spans,
+                           ras.render_span_data );
+
+        #ifdef DEBUG_GRAYS
+
+                if ( 1 )
+                {
+                  int  n;
+
+
+                  fprintf( stderr, "count = %3d ", count );
+                  span = ras.gray_spans;
+                  for ( n = 0; n < count; n++, span++ )
+                    fprintf( stderr, "[%d , %d..%d] : %d ",
+                             span->y, span->x, span->x + span->len - 1, span->coverage );
+                  fprintf( stderr, "\n" );
+                }
+
+        #endif /* DEBUG_GRAYS */
+
+        ras.num_gray_spans = 0;
+
+        span  = ras.gray_spans;
+      }
+      else
+        span++;
+
+      /* add a gray span to the current list */
+      span->x        = (short)x;
+      span->y        = (short)y;
+      span->len      = (unsigned short)acount;
+      span->coverage = (unsigned char)coverage;
+
+      ras.num_gray_spans++;
+    }
+  }
+
+  static void
+  gray_sweep( RAS_ARG)
+  {
+    int  yindex;
+
+    if ( ras.num_cells == 0 )
+      return;
+
+    ras.num_gray_spans = 0;
+
+    for ( yindex = 0; yindex < ras.ycount; yindex++ )
+    {
+      PCell   cell  = ras.ycells[yindex];
+      TCoord  cover = 0;
+      TCoord  x     = 0;
+
+
+      for ( ; cell != NULL; cell = cell->next )
+      {
+        TPos  area;
+
+
+        if ( cell->x > x && cover != 0 )
+          gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+                      cell->x - x );
+
+        cover += cell->cover;
+        area   = cover * ( ONE_PIXEL * 2 ) - cell->area;
+
+        if ( area != 0 && cell->x >= 0 )
+          gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
+
+        x = cell->x + 1;
+      }
+
+      if ( cover != 0 )
+        gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+                    ras.count_ex - x );
+    }
+
+    if ( ras.render_span && ras.num_gray_spans > 0 )
+      ras.render_span(ras.num_gray_spans,
+                       ras.gray_spans, ras.render_span_data );
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /*  The following function should only compile in stand-alone mode,      */
+  /*  i.e., when building this component without the rest of FreeType.     */
+  /*                                                                       */
+  /*************************************************************************/
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    SW_FT_Outline_Decompose                                               */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Walk over an outline's structure to decompose it into individual   */
+  /*    segments and Bézier arcs.  This function is also able to emit      */
+  /*    `move to' and `close to' operations to indicate the start and end  */
+  /*    of new contours in the outline.                                    */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    outline        :: A pointer to the source target.                  */
+  /*                                                                       */
+  /*    func_interface :: A table of `emitters', i.e., function pointers   */
+  /*                      called during decomposition to indicate path     */
+  /*                      operations.                                      */
+  /*                                                                       */
+  /* <InOut>                                                               */
+  /*    user           :: A typeless pointer which is passed to each       */
+  /*                      emitter during the decomposition.  It can be     */
+  /*                      used to store the state during the               */
+  /*                      decomposition.                                   */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    Error code.  0 means success.                                      */
+  /*                                                                       */
+  static int
+  SW_FT_Outline_Decompose( const SW_FT_Outline*        outline,
+                        const SW_FT_Outline_Funcs*  func_interface,
+                        void*                    user )
+  {
+#undef SCALED
+#define SCALED( x )  ( ( (x) << shift ) - delta )
+
+    SW_FT_Vector   v_last;
+    SW_FT_Vector   v_control;
+    SW_FT_Vector   v_start;
+
+    SW_FT_Vector*  point;
+    SW_FT_Vector*  limit;
+    char*       tags;
+
+    int         error;
+
+    int   n;         /* index of contour in outline     */
+    int   first;     /* index of first point in contour */
+    char  tag;       /* current point's state           */
+
+    int   shift;
+    TPos  delta;
+
+
+    if ( !outline || !func_interface )
+      return SW_FT_THROW( Invalid_Argument );
+
+    shift = func_interface->shift;
+    delta = func_interface->delta;
+    first = 0;
+
+    for ( n = 0; n < outline->n_contours; n++ )
+    {
+      int  last;  /* index of last point in contour */
+
+
+      last  = outline->contours[n];
+      if ( last < 0 )
+        goto Invalid_Outline;
+      limit = outline->points + last;
+
+      v_start   = outline->points[first];
+      v_start.x = SCALED( v_start.x );
+      v_start.y = SCALED( v_start.y );
+
+      v_last   = outline->points[last];
+      v_last.x = SCALED( v_last.x );
+      v_last.y = SCALED( v_last.y );
+
+      v_control = v_start;
+
+      point = outline->points + first;
+      tags  = outline->tags   + first;
+      tag   = SW_FT_CURVE_TAG( tags[0] );
+
+      /* A contour cannot start with a cubic control point! */
+      if ( tag == SW_FT_CURVE_TAG_CUBIC )
+        goto Invalid_Outline;
+
+      /* check first point to determine origin */
+      if ( tag == SW_FT_CURVE_TAG_CONIC )
+      {
+        /* first point is conic control.  Yes, this happens. */
+        if ( SW_FT_CURVE_TAG( outline->tags[last] ) == SW_FT_CURVE_TAG_ON )
+        {
+          /* start at last point if it is on the curve */
+          v_start = v_last;
+          limit--;
+        }
+        else
+        {
+          /* if both first and last points are conic,         */
+          /* start at their middle and record its position    */
+          /* for closure                                      */
+          v_start.x = ( v_start.x + v_last.x ) / 2;
+          v_start.y = ( v_start.y + v_last.y ) / 2;
+        }
+        point--;
+        tags--;
+      }
+
+      error = func_interface->move_to( &v_start, user );
+      if ( error )
+        goto Exit;
+
+      while ( point < limit )
+      {
+        point++;
+        tags++;
+
+        tag = SW_FT_CURVE_TAG( tags[0] );
+        switch ( tag )
+        {
+        case SW_FT_CURVE_TAG_ON:  /* emit a single line_to */
+          {
+            SW_FT_Vector  vec;
+
+
+            vec.x = SCALED( point->x );
+            vec.y = SCALED( point->y );
+
+            error = func_interface->line_to( &vec, user );
+            if ( error )
+              goto Exit;
+            continue;
+          }
+
+        case SW_FT_CURVE_TAG_CONIC:  /* consume conic arcs */
+          v_control.x = SCALED( point->x );
+          v_control.y = SCALED( point->y );
+
+        Do_Conic:
+          if ( point < limit )
+          {
+            SW_FT_Vector  vec;
+            SW_FT_Vector  v_middle;
+
+
+            point++;
+            tags++;
+            tag = SW_FT_CURVE_TAG( tags[0] );
+
+            vec.x = SCALED( point->x );
+            vec.y = SCALED( point->y );
+
+            if ( tag == SW_FT_CURVE_TAG_ON )
+            {
+              error = func_interface->conic_to( &v_control, &vec, user );
+              if ( error )
+                goto Exit;
+              continue;
+            }
+
+            if ( tag != SW_FT_CURVE_TAG_CONIC )
+              goto Invalid_Outline;
+
+            v_middle.x = ( v_control.x + vec.x ) / 2;
+            v_middle.y = ( v_control.y + vec.y ) / 2;
+
+            error = func_interface->conic_to( &v_control, &v_middle, user );
+            if ( error )
+              goto Exit;
+
+            v_control = vec;
+            goto Do_Conic;
+          }
+
+          error = func_interface->conic_to( &v_control, &v_start, user );
+          goto Close;
+
+        default:  /* SW_FT_CURVE_TAG_CUBIC */
+          {
+            SW_FT_Vector  vec1, vec2;
+
+
+            if ( point + 1 > limit                             ||
+                 SW_FT_CURVE_TAG( tags[1] ) != SW_FT_CURVE_TAG_CUBIC )
+              goto Invalid_Outline;
+
+            point += 2;
+            tags  += 2;
+
+            vec1.x = SCALED( point[-2].x );
+            vec1.y = SCALED( point[-2].y );
+
+            vec2.x = SCALED( point[-1].x );
+            vec2.y = SCALED( point[-1].y );
+
+            if ( point <= limit )
+            {
+              SW_FT_Vector  vec;
+
+
+              vec.x = SCALED( point->x );
+              vec.y = SCALED( point->y );
+
+              error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
+              if ( error )
+                goto Exit;
+              continue;
+            }
+
+            error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
+            goto Close;
+          }
+        }
+      }
+
+      /* close the contour with a line segment */
+      error = func_interface->line_to( &v_start, user );
+
+   Close:
+      if ( error )
+        goto Exit;
+
+      first = last + 1;
+    }
+
+    return 0;
+
+  Exit:
+    return error;
+
+  Invalid_Outline:
+    return SW_FT_THROW( Invalid_Outline );
+  }
+
+
+  typedef struct  gray_TBand_
+  {
+    TPos  min, max;
+
+  } gray_TBand;
+
+
+
+  SW_FT_DEFINE_OUTLINE_FUNCS(func_interface,
+      (SW_FT_Outline_MoveTo_Func) gray_move_to,
+      (SW_FT_Outline_LineTo_Func) gray_line_to,
+      (SW_FT_Outline_ConicTo_Func)gray_conic_to,
+      (SW_FT_Outline_CubicTo_Func)gray_cubic_to,
+      0,
+      0
+    )
+
+  static int
+  gray_convert_glyph_inner( RAS_ARG )
+  {
+
+    volatile int  error = 0;
+
+    if ( ft_setjmp( ras.jump_buffer ) == 0 )
+    {
+      error = SW_FT_Outline_Decompose( &ras.outline, &func_interface, &ras );
+      if ( !ras.invalid )
+        gray_record_cell( RAS_VAR );
+    }
+    else
+      error = SW_FT_THROW( Memory_Overflow );
+
+    return error;
+  }
+
+
+  static int
+  gray_convert_glyph( RAS_ARG )
+  {
+    gray_TBand            bands[40];
+    gray_TBand* volatile  band;
+    int volatile          n, num_bands;
+    TPos volatile         min, max, max_y;
+    SW_FT_BBox*              clip;
+
+
+    /* Set up state in the raster object */
+    gray_compute_cbox( RAS_VAR );
+
+    /* clip to target bitmap, exit if nothing to do */
+    clip = &ras.clip_box;
+
+    if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
+         ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
+      return 0;
+
+    if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
+    if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
+
+    if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
+    if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
+
+    ras.count_ex = ras.max_ex - ras.min_ex;
+    ras.count_ey = ras.max_ey - ras.min_ey;
+
+    /* set up vertical bands */
+    num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
+    if ( num_bands == 0 )
+      num_bands = 1;
+    if ( num_bands >= 39 )
+      num_bands = 39;
+
+    ras.band_shoot = 0;
+
+    min   = ras.min_ey;
+    max_y = ras.max_ey;
+
+    for ( n = 0; n < num_bands; n++, min = max )
+    {
+      max = min + ras.band_size;
+      if ( n == num_bands - 1 || max > max_y )
+        max = max_y;
+
+      bands[0].min = min;
+      bands[0].max = max;
+      band         = bands;
+
+      while ( band >= bands )
+      {
+        TPos  bottom, top, middle;
+        int   error;
+
+        {
+          PCell  cells_max;
+          int    yindex;
+          long   cell_start, cell_end, cell_mod;
+
+
+          ras.ycells = (PCell*)ras.buffer;
+          ras.ycount = band->max - band->min;
+
+          cell_start = sizeof ( PCell ) * ras.ycount;
+          cell_mod   = cell_start % sizeof ( TCell );
+          if ( cell_mod > 0 )
+            cell_start += sizeof ( TCell ) - cell_mod;
+
+          cell_end  = ras.buffer_size;
+          cell_end -= cell_end % sizeof ( TCell );
+
+          cells_max = (PCell)( (char*)ras.buffer + cell_end );
+          ras.cells = (PCell)( (char*)ras.buffer + cell_start );
+          if ( ras.cells >= cells_max )
+            goto ReduceBands;
+
+          ras.max_cells = cells_max - ras.cells;
+          if ( ras.max_cells < 2 )
+            goto ReduceBands;
+
+          for ( yindex = 0; yindex < ras.ycount; yindex++ )
+            ras.ycells[yindex] = NULL;
+        }
+
+        ras.num_cells = 0;
+        ras.invalid   = 1;
+        ras.min_ey    = band->min;
+        ras.max_ey    = band->max;
+        ras.count_ey  = band->max - band->min;
+
+        error = gray_convert_glyph_inner( RAS_VAR );
+
+        if ( !error )
+        {
+          gray_sweep( RAS_VAR);
+          band--;
+          continue;
+        }
+        else if ( error != ErrRaster_Memory_Overflow )
+          return 1;
+
+      ReduceBands:
+        /* render pool overflow; we will reduce the render band by half */
+        bottom = band->min;
+        top    = band->max;
+        middle = bottom + ( ( top - bottom ) >> 1 );
+
+        /* This is too complex for a single scanline; there must */
+        /* be some problems.                                     */
+        if ( middle == bottom )
+        {
+          return 1;
+        }
+
+        if ( bottom-top >= ras.band_size )
+          ras.band_shoot++;
+
+        band[1].min = bottom;
+        band[1].max = middle;
+        band[0].min = middle;
+        band[0].max = top;
+        band++;
+      }
+    }
+
+    if ( ras.band_shoot > 8 && ras.band_size > 16 )
+      ras.band_size = ras.band_size / 2;
+
+    return 0;
+  }
+
+  static int
+  gray_raster_render( gray_PRaster             raster,
+                      const SW_FT_Raster_Params*  params )
+  {
+    const SW_FT_Outline*  outline     = (const SW_FT_Outline*)params->source;
+
+    gray_TWorker  worker[1];
+
+    TCell  buffer[SW_FT_RENDER_POOL_SIZE / sizeof ( TCell )];
+    long   buffer_size = sizeof ( buffer );
+    int    band_size   = (int)( buffer_size /
+                                (long)( sizeof ( TCell ) * 8 ) );
+
+    if ( !raster)
+      return SW_FT_THROW( Invalid_Argument );
+
+    if ( !outline )
+      return SW_FT_THROW( Invalid_Outline );
+
+    /* return immediately if the outline is empty */
+    if ( outline->n_points == 0 || outline->n_contours <= 0 )
+      return 0;
+
+    if ( !outline->contours || !outline->points )
+      return SW_FT_THROW( Invalid_Outline );
+
+    if ( outline->n_points !=
+           outline->contours[outline->n_contours - 1] + 1 )
+      return SW_FT_THROW( Invalid_Outline );
+
+    /* this version does not support monochrome rendering */
+    if ( !( params->flags & SW_FT_RASTER_FLAG_AA ) )
+      return SW_FT_THROW( Invalid_Mode );
+
+    if ( params->flags & SW_FT_RASTER_FLAG_CLIP )
+      ras.clip_box = params->clip_box;
+    else
+    {
+      ras.clip_box.xMin = -32768L;
+      ras.clip_box.yMin = -32768L;
+      ras.clip_box.xMax =  32767L;
+      ras.clip_box.yMax =  32767L;
+    }
+
+    gray_init_cells( RAS_VAR_ buffer, buffer_size );
+
+    ras.outline        = *outline;
+    ras.num_cells      = 0;
+    ras.invalid        = 1;
+    ras.band_size      = band_size;
+    ras.num_gray_spans = 0;
+
+    ras.render_span      = (SW_FT_Raster_Span_Func)params->gray_spans;
+    ras.render_span_data = params->user;
+
+    return gray_convert_glyph( RAS_VAR );
+  }
+
+  /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/
+  /****                         a static object.                   *****/
+
+  static int
+  gray_raster_new(SW_FT_Raster*  araster )
+  {
+    static gray_TRaster  the_raster;
+
+    *araster = (SW_FT_Raster)&the_raster;
+    SW_FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
+
+    return 0;
+  }
+
+
+  static void
+  gray_raster_done( SW_FT_Raster  raster )
+  {
+    /* nothing */
+    SW_FT_UNUSED( raster );
+  }
+
+  static void
+  gray_raster_reset( SW_FT_Raster  raster,
+                     char*      pool_base,
+                     long       pool_size )
+  {
+    SW_FT_UNUSED( raster );
+    SW_FT_UNUSED( pool_base );
+    SW_FT_UNUSED( pool_size );
+  }
+
+
+  SW_FT_DEFINE_RASTER_FUNCS(sw_ft_grays_raster,
+
+    (SW_FT_Raster_New_Func)     gray_raster_new,
+    (SW_FT_Raster_Reset_Func)   gray_raster_reset,
+    (SW_FT_Raster_Render_Func)  gray_raster_render,
+    (SW_FT_Raster_Done_Func)    gray_raster_done
+  )
+
+
+/* END */
diff --git a/src/vector/freetype/v_ft_raster.h b/src/vector/freetype/v_ft_raster.h
new file mode 100644 (file)
index 0000000..e67f480
--- /dev/null
@@ -0,0 +1,607 @@
+#ifndef V_FT_IMG_H
+#define V_FT_IMG_H
+/***************************************************************************/
+/*                                                                         */
+/*  ftimage.h                                                              */
+/*                                                                         */
+/*    FreeType glyph image formats and default raster interface            */
+/*    (specification).                                                     */
+/*                                                                         */
+/*  Copyright 1996-2010, 2013 by                                           */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* Note: A `raster' is simply a scan-line converter, used to render      */
+  /*       SW_FT_Outlines into SW_FT_Bitmaps.                                    */
+  /*                                                                       */
+  /*************************************************************************/
+
+#include "v_ft_types.h"
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
+  /*    FT_BBox                                                            */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A structure used to hold an outline's bounding box, i.e., the      */
+  /*    coordinates of its extrema in the horizontal and vertical          */
+  /*    directions.                                                        */
+  /*                                                                       */
+  /* <Fields>                                                              */
+  /*    xMin :: The horizontal minimum (left-most).                        */
+  /*                                                                       */
+  /*    yMin :: The vertical minimum (bottom-most).                        */
+  /*                                                                       */
+  /*    xMax :: The horizontal maximum (right-most).                       */
+  /*                                                                       */
+  /*    yMax :: The vertical maximum (top-most).                           */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    The bounding box is specified with the coordinates of the lower    */
+  /*    left and the upper right corner.  In PostScript, those values are  */
+  /*    often called (llx,lly) and (urx,ury), respectively.                */
+  /*                                                                       */
+  /*    If `yMin' is negative, this value gives the glyph's descender.     */
+  /*    Otherwise, the glyph doesn't descend below the baseline.           */
+  /*    Similarly, if `ymax' is positive, this value gives the glyph's     */
+  /*    ascender.                                                          */
+  /*                                                                       */
+  /*    `xMin' gives the horizontal distance from the glyph's origin to    */
+  /*    the left edge of the glyph's bounding box.  If `xMin' is negative, */
+  /*    the glyph extends to the left of the origin.                       */
+  /*                                                                       */
+  typedef struct  SW_FT_BBox_
+  {
+    SW_FT_Pos  xMin, yMin;
+    SW_FT_Pos  xMax, yMax;
+
+  } SW_FT_BBox;
+
+/*************************************************************************/
+/*                                                                       */
+/* <Struct>                                                              */
+/*    SW_FT_Outline                                                      */
+/*                                                                       */
+/* <Description>                                                         */
+/*    This structure is used to describe an outline to the scan-line     */
+/*    converter.                                                         */
+/*                                                                       */
+/* <Fields>                                                              */
+/*    n_contours :: The number of contours in the outline.               */
+/*                                                                       */
+/*    n_points   :: The number of points in the outline.                 */
+/*                                                                       */
+/*    points     :: A pointer to an array of `n_points' @SW_FT_Vector    */
+/*                  elements, giving the outline's point coordinates.    */
+/*                                                                       */
+/*    tags       :: A pointer to an array of `n_points' chars, giving    */
+/*                  each outline point's type.                           */
+/*                                                                       */
+/*                  If bit~0 is unset, the point is `off' the curve,     */
+/*                  i.e., a Bézier control point, while it is `on' if    */
+/*                  set.                                                 */
+/*                                                                       */
+/*                  Bit~1 is meaningful for `off' points only.  If set,  */
+/*                  it indicates a third-order Bézier arc control point; */
+/*                  and a second-order control point if unset.           */
+/*                                                                       */
+/*                  If bit~2 is set, bits 5-7 contain the drop-out mode  */
+/*                  (as defined in the OpenType specification; the value */
+/*                  is the same as the argument to the SCANMODE          */
+/*                  instruction).                                        */
+/*                                                                       */
+/*                  Bits 3 and~4 are reserved for internal purposes.     */
+/*                                                                       */
+/*    contours   :: An array of `n_contours' shorts, giving the end      */
+/*                  point of each contour within the outline.  For       */
+/*                  example, the first contour is defined by the points  */
+/*                  `0' to `contours[0]', the second one is defined by   */
+/*                  the points `contours[0]+1' to `contours[1]', etc.    */
+/*                                                                       */
+/*    flags      :: A set of bit flags used to characterize the outline  */
+/*                  and give hints to the scan-converter and hinter on   */
+/*                  how to convert/grid-fit it.  See @SW_FT_OUTLINE_FLAGS.*/
+/*                                                                       */
+typedef struct  SW_FT_Outline_
+{
+  short       n_contours;      /* number of contours in glyph        */
+  short       n_points;        /* number of points in the glyph      */
+
+  SW_FT_Vector*  points;          /* the outline's points               */
+  char*       tags;            /* the points flags                   */
+  short*      contours;        /* the contour end points             */
+
+  int         flags;           /* outline masks                      */
+
+} SW_FT_Outline;
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Enum>                                                                */
+  /*    SW_FT_OUTLINE_FLAGS                                                   */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A list of bit-field constants use for the flags in an outline's    */
+  /*    `flags' field.                                                     */
+  /*                                                                       */
+  /* <Values>                                                              */
+  /*    SW_FT_OUTLINE_NONE ::                                                 */
+  /*      Value~0 is reserved.                                             */
+  /*                                                                       */
+  /*    SW_FT_OUTLINE_OWNER ::                                                */
+  /*      If set, this flag indicates that the outline's field arrays      */
+  /*      (i.e., `points', `flags', and `contours') are `owned' by the     */
+  /*      outline object, and should thus be freed when it is destroyed.   */
+  /*                                                                       */
+  /*    SW_FT_OUTLINE_EVEN_ODD_FILL ::                                        */
+  /*      By default, outlines are filled using the non-zero winding rule. */
+  /*      If set to 1, the outline will be filled using the even-odd fill  */
+  /*      rule (only works with the smooth rasterizer).                    */
+  /*                                                                       */
+  /*    SW_FT_OUTLINE_REVERSE_FILL ::                                         */
+  /*      By default, outside contours of an outline are oriented in       */
+  /*      clock-wise direction, as defined in the TrueType specification.  */
+  /*      This flag is set if the outline uses the opposite direction      */
+  /*      (typically for Type~1 fonts).  This flag is ignored by the scan  */
+  /*      converter.                                                       */
+  /*                                                                       */
+  /*                                                                       */
+  /*                                                                       */
+  /*    There exists a second mechanism to pass the drop-out mode to the   */
+  /*    B/W rasterizer; see the `tags' field in @SW_FT_Outline.               */
+  /*                                                                       */
+  /*    Please refer to the description of the `SCANTYPE' instruction in   */
+  /*    the OpenType specification (in file `ttinst1.doc') how simple      */
+  /*    drop-outs, smart drop-outs, and stubs are defined.                 */
+  /*                                                                       */
+#define SW_FT_OUTLINE_NONE             0x0
+#define SW_FT_OUTLINE_OWNER            0x1
+#define SW_FT_OUTLINE_EVEN_ODD_FILL    0x2
+#define SW_FT_OUTLINE_REVERSE_FILL     0x4
+
+  /* */
+
+#define SW_FT_CURVE_TAG( flag )  ( flag & 3 )
+
+#define SW_FT_CURVE_TAG_ON            1
+#define SW_FT_CURVE_TAG_CONIC         0
+#define SW_FT_CURVE_TAG_CUBIC         2
+
+
+#define SW_FT_Curve_Tag_On       SW_FT_CURVE_TAG_ON
+#define SW_FT_Curve_Tag_Conic    SW_FT_CURVE_TAG_CONIC
+#define SW_FT_Curve_Tag_Cubic    SW_FT_CURVE_TAG_CUBIC
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* A raster is a scan converter, in charge of rendering an outline into  */
+  /* a a bitmap.  This section contains the public API for rasters.        */
+  /*                                                                       */
+  /* Note that in FreeType 2, all rasters are now encapsulated within      */
+  /* specific modules called `renderers'.  See `ftrender.h' for more       */
+  /* details on renderers.                                                 */
+  /*                                                                       */
+  /*************************************************************************/
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Type>                                                                */
+  /*    SW_FT_Raster                                                          */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A handle (pointer) to a raster object.  Each object can be used    */
+  /*    independently to convert an outline into a bitmap or pixmap.       */
+  /*                                                                       */
+  typedef struct SW_FT_RasterRec_*  SW_FT_Raster;
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
+  /*    SW_FT_Span                                                            */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A structure used to model a single span of gray (or black) pixels  */
+  /*    when rendering a monochrome or anti-aliased bitmap.                */
+  /*                                                                       */
+  /* <Fields>                                                              */
+  /*    x        :: The span's horizontal start position.                  */
+  /*                                                                       */
+  /*    len      :: The span's length in pixels.                           */
+  /*                                                                       */
+  /*    coverage :: The span color/coverage, ranging from 0 (background)   */
+  /*                to 255 (foreground).  Only used for anti-aliased       */
+  /*                rendering.                                             */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    This structure is used by the span drawing callback type named     */
+  /*    @SW_FT_SpanFunc that takes the y~coordinate of the span as a          */
+  /*    parameter.                                                         */
+  /*                                                                       */
+  /*    The coverage value is always between 0 and 255.  If you want less  */
+  /*    gray values, the callback function has to reduce them.             */
+  /*                                                                       */
+  typedef struct  SW_FT_Span_
+  {
+    short           x;
+    short           y;
+    unsigned short  len;
+    unsigned char   coverage;
+
+  } SW_FT_Span;
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
+  /*    SW_FT_SpanFunc                                                        */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A function used as a call-back by the anti-aliased renderer in     */
+  /*    order to let client applications draw themselves the gray pixel    */
+  /*    spans on each scan line.                                           */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    y     :: The scanline's y~coordinate.                              */
+  /*                                                                       */
+  /*    count :: The number of spans to draw on this scanline.             */
+  /*                                                                       */
+  /*    spans :: A table of `count' spans to draw on the scanline.         */
+  /*                                                                       */
+  /*    user  :: User-supplied data that is passed to the callback.        */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    This callback allows client applications to directly render the    */
+  /*    gray spans of the anti-aliased bitmap to any kind of surfaces.     */
+  /*                                                                       */
+  /*    This can be used to write anti-aliased outlines directly to a      */
+  /*    given background bitmap, and even perform translucency.            */
+  /*                                                                       */
+  /*    Note that the `count' field cannot be greater than a fixed value   */
+  /*    defined by the `SW_FT_MAX_GRAY_SPANS' configuration macro in          */
+  /*    `ftoption.h'.  By default, this value is set to~32, which means    */
+  /*    that if there are more than 32~spans on a given scanline, the      */
+  /*    callback is called several times with the same `y' parameter in    */
+  /*    order to draw all callbacks.                                       */
+  /*                                                                       */
+  /*    Otherwise, the callback is only called once per scan-line, and     */
+  /*    only for those scanlines that do have `gray' pixels on them.       */
+  /*                                                                       */
+  typedef void
+  (*SW_FT_SpanFunc)( int             count,
+                  const SW_FT_Span*  spans,
+                  void*           user );
+
+#define SW_FT_Raster_Span_Func  SW_FT_SpanFunc
+
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Enum>                                                                */
+  /*    SW_FT_RASTER_FLAG_XXX                                                 */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A list of bit flag constants as used in the `flags' field of a     */
+  /*    @SW_FT_Raster_Params structure.                                       */
+  /*                                                                       */
+  /* <Values>                                                              */
+  /*    SW_FT_RASTER_FLAG_DEFAULT :: This value is 0.                         */
+  /*                                                                       */
+  /*    SW_FT_RASTER_FLAG_AA      :: This flag is set to indicate that an     */
+  /*                              anti-aliased glyph image should be       */
+  /*                              generated.  Otherwise, it will be        */
+  /*                              monochrome (1-bit).                      */
+  /*                                                                       */
+  /*    SW_FT_RASTER_FLAG_DIRECT  :: This flag is set to indicate direct      */
+  /*                              rendering.  In this mode, client         */
+  /*                              applications must provide their own span */
+  /*                              callback.  This lets them directly       */
+  /*                              draw or compose over an existing bitmap. */
+  /*                              If this bit is not set, the target       */
+  /*                              pixmap's buffer _must_ be zeroed before  */
+  /*                              rendering.                               */
+  /*                                                                       */
+  /*                              Note that for now, direct rendering is   */
+  /*                              only possible with anti-aliased glyphs.  */
+  /*                                                                       */
+  /*    SW_FT_RASTER_FLAG_CLIP    :: This flag is only used in direct         */
+  /*                              rendering mode.  If set, the output will */
+  /*                              be clipped to a box specified in the     */
+  /*                              `clip_box' field of the                  */
+  /*                              @SW_FT_Raster_Params structure.             */
+  /*                                                                       */
+  /*                              Note that by default, the glyph bitmap   */
+  /*                              is clipped to the target pixmap, except  */
+  /*                              in direct rendering mode where all spans */
+  /*                              are generated if no clipping box is set. */
+  /*                                                                       */
+#define SW_FT_RASTER_FLAG_DEFAULT  0x0
+#define SW_FT_RASTER_FLAG_AA       0x1
+#define SW_FT_RASTER_FLAG_DIRECT   0x2
+#define SW_FT_RASTER_FLAG_CLIP     0x4
+
+  /* deprecated */
+#define ft_raster_flag_default  SW_FT_RASTER_FLAG_DEFAULT
+#define ft_raster_flag_aa       SW_FT_RASTER_FLAG_AA
+#define ft_raster_flag_direct   SW_FT_RASTER_FLAG_DIRECT
+#define ft_raster_flag_clip     SW_FT_RASTER_FLAG_CLIP
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
+  /*    SW_FT_Raster_Params                                                   */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A structure to hold the arguments used by a raster's render        */
+  /*    function.                                                          */
+  /*                                                                       */
+  /* <Fields>                                                              */
+  /*    target      :: The target bitmap.                                  */
+  /*                                                                       */
+  /*    source      :: A pointer to the source glyph image (e.g., an       */
+  /*                   @SW_FT_Outline).                                       */
+  /*                                                                       */
+  /*    flags       :: The rendering flags.                                */
+  /*                                                                       */
+  /*    gray_spans  :: The gray span drawing callback.                     */
+  /*                                                                       */
+  /*    black_spans :: The black span drawing callback.  UNIMPLEMENTED!    */
+  /*                                                                       */
+  /*    bit_test    :: The bit test callback.  UNIMPLEMENTED!              */
+  /*                                                                       */
+  /*    bit_set     :: The bit set callback.  UNIMPLEMENTED!               */
+  /*                                                                       */
+  /*    user        :: User-supplied data that is passed to each drawing   */
+  /*                   callback.                                           */
+  /*                                                                       */
+  /*    clip_box    :: An optional clipping box.  It is only used in       */
+  /*                   direct rendering mode.  Note that coordinates here  */
+  /*                   should be expressed in _integer_ pixels (and not in */
+  /*                   26.6 fixed-point units).                            */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    An anti-aliased glyph bitmap is drawn if the @SW_FT_RASTER_FLAG_AA    */
+  /*    bit flag is set in the `flags' field, otherwise a monochrome       */
+  /*    bitmap is generated.                                               */
+  /*                                                                       */
+  /*    If the @SW_FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the      */
+  /*    raster will call the `gray_spans' callback to draw gray pixel      */
+  /*    spans, in the case of an aa glyph bitmap, it will call             */
+  /*    `black_spans', and `bit_test' and `bit_set' in the case of a       */
+  /*    monochrome bitmap.  This allows direct composition over a          */
+  /*    pre-existing bitmap through user-provided callbacks to perform the */
+  /*    span drawing/composition.                                          */
+  /*                                                                       */
+  /*    Note that the `bit_test' and `bit_set' callbacks are required when */
+  /*    rendering a monochrome bitmap, as they are crucial to implement    */
+  /*    correct drop-out control as defined in the TrueType specification. */
+  /*                                                                       */
+  typedef struct  SW_FT_Raster_Params_
+  {
+    const void*             source;
+    int                     flags;
+    SW_FT_SpanFunc          gray_spans;
+    void*                   user;
+    SW_FT_BBox              clip_box;
+
+  } SW_FT_Raster_Params;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Function>                                                            */
+/*    SW_FT_Outline_Check                                                   */
+/*                                                                       */
+/* <Description>                                                         */
+/*    Check the contents of an outline descriptor.                       */
+/*                                                                       */
+/* <Input>                                                               */
+/*    outline :: A handle to a source outline.                           */
+/*                                                                       */
+/* <Return>                                                              */
+/*    FreeType error code.  0~means success.                             */
+/*                                                                       */
+SW_FT_Error
+SW_FT_Outline_Check( SW_FT_Outline*  outline );
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Function>                                                            */
+/*    SW_FT_Outline_Get_CBox                                                */
+/*                                                                       */
+/* <Description>                                                         */
+/*    Return an outline's `control box'.  The control box encloses all   */
+/*    the outline's points, including Bézier control points.  Though it  */
+/*    coincides with the exact bounding box for most glyphs, it can be   */
+/*    slightly larger in some situations (like when rotating an outline  */
+/*    that contains Bézier outside arcs).                                */
+/*                                                                       */
+/*    Computing the control box is very fast, while getting the bounding */
+/*    box can take much more time as it needs to walk over all segments  */
+/*    and arcs in the outline.  To get the latter, you can use the       */
+/*    `ftbbox' component, which is dedicated to this single task.        */
+/*                                                                       */
+/* <Input>                                                               */
+/*    outline :: A pointer to the source outline descriptor.             */
+/*                                                                       */
+/* <Output>                                                              */
+/*    acbox   :: The outline's control box.                              */
+/*                                                                       */
+/* <Note>                                                                */
+/*    See @SW_FT_Glyph_Get_CBox for a discussion of tricky fonts.           */
+/*                                                                       */
+void
+SW_FT_Outline_Get_CBox( const SW_FT_Outline*  outline,
+                     SW_FT_BBox           *acbox );
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
+  /*    SW_FT_Raster_NewFunc                                                  */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A function used to create a new raster object.                     */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    memory :: A handle to the memory allocator.                        */
+  /*                                                                       */
+  /* <Output>                                                              */
+  /*    raster :: A handle to the new raster object.                       */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    Error code.  0~means success.                                      */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    The `memory' parameter is a typeless pointer in order to avoid     */
+  /*    un-wanted dependencies on the rest of the FreeType code.  In       */
+  /*    practice, it is an @SW_FT_Memory object, i.e., a handle to the        */
+  /*    standard FreeType memory allocator.  However, this field can be    */
+  /*    completely ignored by a given raster implementation.               */
+  /*                                                                       */
+  typedef int
+  (*SW_FT_Raster_NewFunc)( SW_FT_Raster*  raster );
+
+#define SW_FT_Raster_New_Func  SW_FT_Raster_NewFunc
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
+  /*    SW_FT_Raster_DoneFunc                                                 */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A function used to destroy a given raster object.                  */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    raster :: A handle to the raster object.                           */
+  /*                                                                       */
+  typedef void
+  (*SW_FT_Raster_DoneFunc)( SW_FT_Raster  raster );
+
+#define SW_FT_Raster_Done_Func  SW_FT_Raster_DoneFunc
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
+  /*    SW_FT_Raster_ResetFunc                                                */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    FreeType provides an area of memory called the `render pool',      */
+  /*    available to all registered rasters.  This pool can be freely used */
+  /*    during a given scan-conversion but is shared by all rasters.  Its  */
+  /*    content is thus transient.                                         */
+  /*                                                                       */
+  /*    This function is called each time the render pool changes, or just */
+  /*    after a new raster object is created.                              */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    raster    :: A handle to the new raster object.                    */
+  /*                                                                       */
+  /*    pool_base :: The address in memory of the render pool.             */
+  /*                                                                       */
+  /*    pool_size :: The size in bytes of the render pool.                 */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    Rasters can ignore the render pool and rely on dynamic memory      */
+  /*    allocation if they want to (a handle to the memory allocator is    */
+  /*    passed to the raster constructor).  However, this is not           */
+  /*    recommended for efficiency purposes.                               */
+  /*                                                                       */
+  typedef void
+  (*SW_FT_Raster_ResetFunc)( SW_FT_Raster       raster,
+                          unsigned char*  pool_base,
+                          unsigned long   pool_size );
+
+#define SW_FT_Raster_Reset_Func  SW_FT_Raster_ResetFunc
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <FuncType>                                                            */
+  /*    SW_FT_Raster_RenderFunc                                               */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Invoke a given raster to scan-convert a given glyph image into a   */
+  /*    target bitmap.                                                     */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    raster :: A handle to the raster object.                           */
+  /*                                                                       */
+  /*    params :: A pointer to an @SW_FT_Raster_Params structure used to      */
+  /*              store the rendering parameters.                          */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    Error code.  0~means success.                                      */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    The exact format of the source image depends on the raster's glyph */
+  /*    format defined in its @SW_FT_Raster_Funcs structure.  It can be an    */
+  /*    @SW_FT_Outline or anything else in order to support a large array of  */
+  /*    glyph formats.                                                     */
+  /*                                                                       */
+  /*    Note also that the render function can fail and return a           */
+  /*    `SW_FT_Err_Unimplemented_Feature' error code if the raster used does  */
+  /*    not support direct composition.                                    */
+  /*                                                                       */
+  /*    XXX: For now, the standard raster doesn't support direct           */
+  /*         composition but this should change for the final release (see */
+  /*         the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c'    */
+  /*         for examples of distinct implementations that support direct  */
+  /*         composition).                                                 */
+  /*                                                                       */
+  typedef int
+  (*SW_FT_Raster_RenderFunc)( SW_FT_Raster                raster,
+                           const SW_FT_Raster_Params*  params );
+
+#define SW_FT_Raster_Render_Func  SW_FT_Raster_RenderFunc
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
+  /*    SW_FT_Raster_Funcs                                                    */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*   A structure used to describe a given raster class to the library.   */
+  /*                                                                       */
+  /* <Fields>                                                              */
+  /*    glyph_format  :: The supported glyph format for this raster.       */
+  /*                                                                       */
+  /*    raster_new    :: The raster constructor.                           */
+  /*                                                                       */
+  /*    raster_reset  :: Used to reset the render pool within the raster.  */
+  /*                                                                       */
+  /*    raster_render :: A function to render a glyph into a given bitmap. */
+  /*                                                                       */
+  /*    raster_done   :: The raster destructor.                            */
+  /*                                                                       */
+  typedef struct  SW_FT_Raster_Funcs_
+  {
+    SW_FT_Raster_NewFunc      raster_new;
+    SW_FT_Raster_ResetFunc    raster_reset;
+    SW_FT_Raster_RenderFunc   raster_render;
+    SW_FT_Raster_DoneFunc     raster_done;
+
+  } SW_FT_Raster_Funcs;
+
+
+extern const SW_FT_Raster_Funcs   sw_ft_grays_raster;
+
+#endif // V_FT_IMG_H
diff --git a/src/vector/freetype/v_ft_stroker.cpp b/src/vector/freetype/v_ft_stroker.cpp
new file mode 100644 (file)
index 0000000..3315262
--- /dev/null
@@ -0,0 +1,2292 @@
+
+/***************************************************************************/
+/*                                                                         */
+/*  ftstroke.c                                                             */
+/*                                                                         */
+/*    FreeType path stroker (body).                                        */
+/*                                                                         */
+/*  Copyright 2002-2006, 2008-2011, 2013 by                                */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "v_ft_math.h"
+#include "v_ft_stroker.h"
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                      BEZIER COMPUTATIONS                      *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+#define SW_FT_SMALL_CONIC_THRESHOLD  ( SW_FT_ANGLE_PI / 6 )
+#define SW_FT_SMALL_CUBIC_THRESHOLD  ( SW_FT_ANGLE_PI / 8 )
+
+#define SW_FT_EPSILON  2
+
+#define SW_FT_IS_SMALL( x )  ( (x) > -SW_FT_EPSILON && (x) < SW_FT_EPSILON )
+
+
+  static SW_FT_Pos
+  ft_pos_abs( SW_FT_Pos  x )
+  {
+    return x >= 0 ? x : -x;
+  }
+
+
+  static void
+  ft_conic_split( SW_FT_Vector*  base )
+  {
+    SW_FT_Pos  a, b;
+
+
+    base[4].x = base[2].x;
+    b = base[1].x;
+    a = base[3].x = ( base[2].x + b ) / 2;
+    b = base[1].x = ( base[0].x + b ) / 2;
+    base[2].x = ( a + b ) / 2;
+
+    base[4].y = base[2].y;
+    b = base[1].y;
+    a = base[3].y = ( base[2].y + b ) / 2;
+    b = base[1].y = ( base[0].y + b ) / 2;
+    base[2].y = ( a + b ) / 2;
+  }
+
+
+  static SW_FT_Bool
+  ft_conic_is_small_enough( SW_FT_Vector*  base,
+                            SW_FT_Angle   *angle_in,
+                            SW_FT_Angle   *angle_out )
+  {
+    SW_FT_Vector  d1, d2;
+    SW_FT_Angle   theta;
+    SW_FT_Int     close1, close2;
+
+
+    d1.x = base[1].x - base[2].x;
+    d1.y = base[1].y - base[2].y;
+    d2.x = base[0].x - base[1].x;
+    d2.y = base[0].y - base[1].y;
+
+    close1 = SW_FT_IS_SMALL( d1.x ) && SW_FT_IS_SMALL( d1.y );
+    close2 = SW_FT_IS_SMALL( d2.x ) && SW_FT_IS_SMALL( d2.y );
+
+    if ( close1 )
+    {
+      if ( close2 )
+      {
+        /* basically a point;                      */
+        /* do nothing to retain original direction */
+      }
+      else
+      {
+        *angle_in  =
+        *angle_out = SW_FT_Atan2( d2.x, d2.y );
+      }
+    }
+    else /* !close1 */
+    {
+      if ( close2 )
+      {
+        *angle_in  =
+        *angle_out = SW_FT_Atan2( d1.x, d1.y );
+      }
+      else
+      {
+        *angle_in  = SW_FT_Atan2( d1.x, d1.y );
+        *angle_out = SW_FT_Atan2( d2.x, d2.y );
+      }
+    }
+
+    theta = ft_pos_abs( SW_FT_Angle_Diff( *angle_in, *angle_out ) );
+
+    return SW_FT_BOOL( theta < SW_FT_SMALL_CONIC_THRESHOLD );
+  }
+
+
+  static void
+  ft_cubic_split( SW_FT_Vector*  base )
+  {
+    SW_FT_Pos  a, b, c, d;
+
+
+    base[6].x = base[3].x;
+    c = base[1].x;
+    d = base[2].x;
+    base[1].x = a = ( base[0].x + c ) / 2;
+    base[5].x = b = ( base[3].x + d ) / 2;
+    c = ( c + d ) / 2;
+    base[2].x = a = ( a + c ) / 2;
+    base[4].x = b = ( b + c ) / 2;
+    base[3].x = ( a + b ) / 2;
+
+    base[6].y = base[3].y;
+    c = base[1].y;
+    d = base[2].y;
+    base[1].y = a = ( base[0].y + c ) / 2;
+    base[5].y = b = ( base[3].y + d ) / 2;
+    c = ( c + d ) / 2;
+    base[2].y = a = ( a + c ) / 2;
+    base[4].y = b = ( b + c ) / 2;
+    base[3].y = ( a + b ) / 2;
+  }
+
+
+  /* Return the average of `angle1' and `angle2'.            */
+  /* This gives correct result even if `angle1' and `angle2' */
+  /* have opposite signs.                                    */
+  static SW_FT_Angle
+  ft_angle_mean( SW_FT_Angle  angle1,
+                 SW_FT_Angle  angle2 )
+  {
+    return angle1 + SW_FT_Angle_Diff( angle1, angle2 ) / 2;
+  }
+
+
+  static SW_FT_Bool
+  ft_cubic_is_small_enough( SW_FT_Vector*  base,
+                            SW_FT_Angle   *angle_in,
+                            SW_FT_Angle   *angle_mid,
+                            SW_FT_Angle   *angle_out )
+  {
+    SW_FT_Vector  d1, d2, d3;
+    SW_FT_Angle   theta1, theta2;
+    SW_FT_Int     close1, close2, close3;
+
+
+    d1.x = base[2].x - base[3].x;
+    d1.y = base[2].y - base[3].y;
+    d2.x = base[1].x - base[2].x;
+    d2.y = base[1].y - base[2].y;
+    d3.x = base[0].x - base[1].x;
+    d3.y = base[0].y - base[1].y;
+
+    close1 = SW_FT_IS_SMALL( d1.x ) && SW_FT_IS_SMALL( d1.y );
+    close2 = SW_FT_IS_SMALL( d2.x ) && SW_FT_IS_SMALL( d2.y );
+    close3 = SW_FT_IS_SMALL( d3.x ) && SW_FT_IS_SMALL( d3.y );
+
+    if ( close1 )
+    {
+      if ( close2 )
+      {
+        if ( close3 )
+        {
+          /* basically a point;                      */
+          /* do nothing to retain original direction */
+        }
+        else /* !close3 */
+        {
+          *angle_in  =
+          *angle_mid =
+          *angle_out = SW_FT_Atan2( d3.x, d3.y );
+        }
+      }
+      else /* !close2 */
+      {
+        if ( close3 )
+        {
+          *angle_in  =
+          *angle_mid =
+          *angle_out = SW_FT_Atan2( d2.x, d2.y );
+        }
+        else /* !close3 */
+        {
+          *angle_in  =
+          *angle_mid = SW_FT_Atan2( d2.x, d2.y );
+          *angle_out = SW_FT_Atan2( d3.x, d3.y );
+        }
+      }
+    }
+    else /* !close1 */
+    {
+      if ( close2 )
+      {
+        if ( close3 )
+        {
+          *angle_in  =
+          *angle_mid =
+          *angle_out = SW_FT_Atan2( d1.x, d1.y );
+        }
+        else /* !close3 */
+        {
+          *angle_in  = SW_FT_Atan2( d1.x, d1.y );
+          *angle_out = SW_FT_Atan2( d3.x, d3.y );
+          *angle_mid = ft_angle_mean( *angle_in, *angle_out );
+        }
+      }
+      else /* !close2 */
+      {
+        if ( close3 )
+        {
+          *angle_in  = SW_FT_Atan2( d1.x, d1.y );
+          *angle_mid =
+          *angle_out = SW_FT_Atan2( d2.x, d2.y );
+        }
+        else /* !close3 */
+        {
+          *angle_in  = SW_FT_Atan2( d1.x, d1.y );
+          *angle_mid = SW_FT_Atan2( d2.x, d2.y );
+          *angle_out = SW_FT_Atan2( d3.x, d3.y );
+        }
+      }
+    }
+
+    theta1 = ft_pos_abs( SW_FT_Angle_Diff( *angle_in,  *angle_mid ) );
+    theta2 = ft_pos_abs( SW_FT_Angle_Diff( *angle_mid, *angle_out ) );
+
+    return SW_FT_BOOL( theta1 < SW_FT_SMALL_CUBIC_THRESHOLD &&
+                    theta2 < SW_FT_SMALL_CUBIC_THRESHOLD );
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                       STROKE BORDERS                          *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  typedef enum  SW_FT_StrokeTags_
+  {
+    SW_FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
+    SW_FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
+    SW_FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
+    SW_FT_STROKE_TAG_END   = 8    /* sub-path end    */
+
+  } SW_FT_StrokeTags;
+
+#define  SW_FT_STROKE_TAG_BEGIN_END  ( SW_FT_STROKE_TAG_BEGIN | SW_FT_STROKE_TAG_END )
+
+  typedef struct  SW_FT_StrokeBorderRec_
+  {
+    SW_FT_UInt     num_points;
+    SW_FT_UInt     max_points;
+    SW_FT_Vector*  points;
+    SW_FT_Byte*    tags;
+    SW_FT_Bool     movable;  /* TRUE for ends of lineto borders */
+    SW_FT_Int      start;    /* index of current sub-path start point */
+    SW_FT_Bool     valid;
+
+  } SW_FT_StrokeBorderRec, *SW_FT_StrokeBorder;
+
+
+
+  SW_FT_Error
+  SW_FT_Outline_Check( SW_FT_Outline*  outline )
+  {
+    if ( outline )
+    {
+      SW_FT_Int  n_points   = outline->n_points;
+      SW_FT_Int  n_contours = outline->n_contours;
+      SW_FT_Int  end0, end;
+      SW_FT_Int  n;
+
+
+      /* empty glyph? */
+      if ( n_points == 0 && n_contours == 0 )
+        return 0;
+
+      /* check point and contour counts */
+      if ( n_points <= 0 || n_contours <= 0 )
+        goto Bad;
+
+      end0 = end = -1;
+      for ( n = 0; n < n_contours; n++ )
+      {
+        end = outline->contours[n];
+
+        /* note that we don't accept empty contours */
+        if ( end <= end0 || end >= n_points )
+          goto Bad;
+
+        end0 = end;
+      }
+
+      if ( end != n_points - 1 )
+        goto Bad;
+
+      /* XXX: check the tags array */
+      return 0;
+    }
+
+  Bad:
+    return -1;//SW_FT_THROW( Invalid_Argument );
+  }
+
+
+
+  void
+  SW_FT_Outline_Get_CBox( const SW_FT_Outline*  outline,
+                       SW_FT_BBox           *acbox )
+  {
+    SW_FT_Pos  xMin, yMin, xMax, yMax;
+
+
+    if ( outline && acbox )
+    {
+      if ( outline->n_points == 0 )
+      {
+        xMin = 0;
+        yMin = 0;
+        xMax = 0;
+        yMax = 0;
+      }
+      else
+      {
+        SW_FT_Vector*  vec   = outline->points;
+        SW_FT_Vector*  limit = vec + outline->n_points;
+
+
+        xMin = xMax = vec->x;
+        yMin = yMax = vec->y;
+        vec++;
+
+        for ( ; vec < limit; vec++ )
+        {
+          SW_FT_Pos  x, y;
+
+
+          x = vec->x;
+          if ( x < xMin ) xMin = x;
+          if ( x > xMax ) xMax = x;
+
+          y = vec->y;
+          if ( y < yMin ) yMin = y;
+          if ( y > yMax ) yMax = y;
+        }
+      }
+      acbox->xMin = xMin;
+      acbox->xMax = xMax;
+      acbox->yMin = yMin;
+      acbox->yMax = yMax;
+    }
+  }
+
+
+
+  static SW_FT_Error
+  ft_stroke_border_grow( SW_FT_StrokeBorder  border,
+                         SW_FT_UInt          new_points )
+  {
+    SW_FT_UInt   old_max = border->max_points;
+    SW_FT_UInt   new_max = border->num_points + new_points;
+    SW_FT_Error  error   = 0;
+
+
+    if ( new_max > old_max )
+    {
+      SW_FT_UInt    cur_max = old_max;
+
+
+      while ( cur_max < new_max )
+        cur_max += ( cur_max >> 1 ) + 16;
+
+      border->points = (SW_FT_Vector *) realloc(border->points, cur_max * sizeof(SW_FT_Vector));
+      border->tags = (SW_FT_Byte *) realloc(border->tags, cur_max * sizeof(SW_FT_Byte));
+
+      if ( !border->points || !border->tags)
+        goto Exit;
+
+      border->max_points = cur_max;
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  static void
+  ft_stroke_border_close( SW_FT_StrokeBorder  border,
+                          SW_FT_Bool          reverse )
+  {
+    SW_FT_UInt  start = border->start;
+    SW_FT_UInt  count = border->num_points;
+
+
+    assert( border->start >= 0 );
+
+    /* don't record empty paths! */
+    if ( count <= start + 1U )
+      border->num_points = start;
+    else
+    {
+      /* copy the last point to the start of this sub-path, since */
+      /* it contains the `adjusted' starting coordinates          */
+      border->num_points    = --count;
+      border->points[start] = border->points[count];
+
+      if ( reverse )
+      {
+        /* reverse the points */
+        {
+          SW_FT_Vector*  vec1 = border->points + start + 1;
+          SW_FT_Vector*  vec2 = border->points + count - 1;
+
+
+          for ( ; vec1 < vec2; vec1++, vec2-- )
+          {
+            SW_FT_Vector  tmp;
+
+
+            tmp   = *vec1;
+            *vec1 = *vec2;
+            *vec2 = tmp;
+          }
+        }
+
+        /* then the tags */
+        {
+          SW_FT_Byte*  tag1 = border->tags + start + 1;
+          SW_FT_Byte*  tag2 = border->tags + count - 1;
+
+
+          for ( ; tag1 < tag2; tag1++, tag2-- )
+          {
+            SW_FT_Byte  tmp;
+
+
+            tmp   = *tag1;
+            *tag1 = *tag2;
+            *tag2 = tmp;
+          }
+        }
+      }
+
+      border->tags[start    ] |= SW_FT_STROKE_TAG_BEGIN;
+      border->tags[count - 1] |= SW_FT_STROKE_TAG_END;
+    }
+
+    border->start   = -1;
+    border->movable = FALSE;
+  }
+
+
+  static SW_FT_Error
+  ft_stroke_border_lineto( SW_FT_StrokeBorder  border,
+                           SW_FT_Vector*       to,
+                           SW_FT_Bool          movable )
+  {
+    SW_FT_Error  error = 0;
+
+
+    assert( border->start >= 0 );
+
+    if ( border->movable )
+    {
+      /* move last point */
+      border->points[border->num_points - 1] = *to;
+    }
+    else
+    {
+      /* don't add zero-length lineto */
+      if ( border->num_points > 0                                          &&
+           SW_FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) &&
+           SW_FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) )
+        return error;
+
+      /* add one point */
+      error = ft_stroke_border_grow( border, 1 );
+      if ( !error )
+      {
+        SW_FT_Vector*  vec = border->points + border->num_points;
+        SW_FT_Byte*    tag = border->tags   + border->num_points;
+
+
+        vec[0] = *to;
+        tag[0] = SW_FT_STROKE_TAG_ON;
+
+        border->num_points += 1;
+      }
+    }
+    border->movable = movable;
+    return error;
+  }
+
+
+  static SW_FT_Error
+  ft_stroke_border_conicto( SW_FT_StrokeBorder  border,
+                            SW_FT_Vector*       control,
+                            SW_FT_Vector*       to )
+  {
+    SW_FT_Error  error;
+
+
+    assert( border->start >= 0 );
+
+    error = ft_stroke_border_grow( border, 2 );
+    if ( !error )
+    {
+      SW_FT_Vector*  vec = border->points + border->num_points;
+      SW_FT_Byte*    tag = border->tags   + border->num_points;
+
+
+      vec[0] = *control;
+      vec[1] = *to;
+
+      tag[0] = 0;
+      tag[1] = SW_FT_STROKE_TAG_ON;
+
+      border->num_points += 2;
+    }
+
+    border->movable = FALSE;
+
+    return error;
+  }
+
+
+  static SW_FT_Error
+  ft_stroke_border_cubicto( SW_FT_StrokeBorder  border,
+                            SW_FT_Vector*       control1,
+                            SW_FT_Vector*       control2,
+                            SW_FT_Vector*       to )
+  {
+    SW_FT_Error  error;
+
+
+    assert( border->start >= 0 );
+
+    error = ft_stroke_border_grow( border, 3 );
+    if ( !error )
+    {
+      SW_FT_Vector*  vec = border->points + border->num_points;
+      SW_FT_Byte*    tag = border->tags   + border->num_points;
+
+
+      vec[0] = *control1;
+      vec[1] = *control2;
+      vec[2] = *to;
+
+      tag[0] = SW_FT_STROKE_TAG_CUBIC;
+      tag[1] = SW_FT_STROKE_TAG_CUBIC;
+      tag[2] = SW_FT_STROKE_TAG_ON;
+
+      border->num_points += 3;
+    }
+
+    border->movable = FALSE;
+
+    return error;
+  }
+
+
+#define SW_FT_ARC_CUBIC_ANGLE  ( SW_FT_ANGLE_PI / 2 )
+
+
+  static SW_FT_Error
+  ft_stroke_border_arcto( SW_FT_StrokeBorder  border,
+                          SW_FT_Vector*       center,
+                          SW_FT_Fixed         radius,
+                          SW_FT_Angle         angle_start,
+                          SW_FT_Angle         angle_diff )
+  {
+    SW_FT_Angle   total, angle, step, rotate, next, theta;
+    SW_FT_Vector  a, b, a2, b2;
+    SW_FT_Fixed   length;
+    SW_FT_Error   error = 0;
+
+
+    /* compute start point */
+    SW_FT_Vector_From_Polar( &a, radius, angle_start );
+    a.x += center->x;
+    a.y += center->y;
+
+    total  = angle_diff;
+    angle  = angle_start;
+    rotate = ( angle_diff >= 0 ) ? SW_FT_ANGLE_PI2 : -SW_FT_ANGLE_PI2;
+
+    while ( total != 0 )
+    {
+      step = total;
+      if ( step > SW_FT_ARC_CUBIC_ANGLE )
+        step = SW_FT_ARC_CUBIC_ANGLE;
+
+      else if ( step < -SW_FT_ARC_CUBIC_ANGLE )
+        step = -SW_FT_ARC_CUBIC_ANGLE;
+
+      next  = angle + step;
+      theta = step;
+      if ( theta < 0 )
+        theta = -theta;
+
+      theta >>= 1;
+
+      /* compute end point */
+      SW_FT_Vector_From_Polar( &b, radius, next );
+      b.x += center->x;
+      b.y += center->y;
+
+      /* compute first and second control points */
+      length = SW_FT_MulDiv( radius, SW_FT_Sin( theta ) * 4,
+                          ( 0x10000L + SW_FT_Cos( theta ) ) * 3 );
+
+      SW_FT_Vector_From_Polar( &a2, length, angle + rotate );
+      a2.x += a.x;
+      a2.y += a.y;
+
+      SW_FT_Vector_From_Polar( &b2, length, next - rotate );
+      b2.x += b.x;
+      b2.y += b.y;
+
+      /* add cubic arc */
+      error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
+      if ( error )
+        break;
+
+      /* process the rest of the arc ?? */
+      a      = b;
+      total -= step;
+      angle  = next;
+    }
+
+    return error;
+  }
+
+
+  static SW_FT_Error
+  ft_stroke_border_moveto( SW_FT_StrokeBorder  border,
+                           SW_FT_Vector*       to )
+  {
+    /* close current open path if any ? */
+    if ( border->start >= 0 )
+      ft_stroke_border_close( border, FALSE );
+
+    border->start = border->num_points;
+    border->movable = FALSE;
+
+    return ft_stroke_border_lineto( border, to, FALSE );
+  }
+
+
+  static void
+  ft_stroke_border_init( SW_FT_StrokeBorder  border)
+  {
+    border->points = NULL;
+    border->tags   = NULL;
+
+    border->num_points = 0;
+    border->max_points = 0;
+    border->start      = -1;
+    border->valid      = FALSE;
+  }
+
+
+  static void
+  ft_stroke_border_reset( SW_FT_StrokeBorder  border )
+  {
+    border->num_points = 0;
+    border->start      = -1;
+    border->valid      = FALSE;
+  }
+
+
+  static void
+  ft_stroke_border_done( SW_FT_StrokeBorder  border )
+  {
+
+    free( border->points );
+    free( border->tags );
+
+    border->num_points = 0;
+    border->max_points = 0;
+    border->start      = -1;
+    border->valid      = FALSE;
+  }
+
+
+  static SW_FT_Error
+  ft_stroke_border_get_counts( SW_FT_StrokeBorder  border,
+                               SW_FT_UInt         *anum_points,
+                               SW_FT_UInt         *anum_contours )
+  {
+    SW_FT_Error  error        = 0;
+    SW_FT_UInt   num_points   = 0;
+    SW_FT_UInt   num_contours = 0;
+
+    SW_FT_UInt     count      = border->num_points;
+    SW_FT_Vector*  point      = border->points;
+    SW_FT_Byte*    tags       = border->tags;
+    SW_FT_Int      in_contour = 0;
+
+
+    for ( ; count > 0; count--, num_points++, point++, tags++ )
+    {
+      if ( tags[0] & SW_FT_STROKE_TAG_BEGIN )
+      {
+        if ( in_contour != 0 )
+          goto Fail;
+
+        in_contour = 1;
+      }
+      else if ( in_contour == 0 )
+        goto Fail;
+
+      if ( tags[0] & SW_FT_STROKE_TAG_END )
+      {
+        in_contour = 0;
+        num_contours++;
+      }
+    }
+
+    if ( in_contour != 0 )
+      goto Fail;
+
+    border->valid = TRUE;
+
+  Exit:
+    *anum_points   = num_points;
+    *anum_contours = num_contours;
+    return error;
+
+  Fail:
+    num_points   = 0;
+    num_contours = 0;
+    goto Exit;
+  }
+
+
+  static void
+  ft_stroke_border_export( SW_FT_StrokeBorder  border,
+                           SW_FT_Outline*      outline )
+  {
+    /* copy point locations */
+    memcpy( outline->points + outline->n_points,
+                   border->points,
+                   border->num_points * sizeof(SW_FT_Vector));
+
+    /* copy tags */
+    {
+      SW_FT_UInt   count = border->num_points;
+      SW_FT_Byte*  read  = border->tags;
+      SW_FT_Byte*  write = (SW_FT_Byte*)outline->tags + outline->n_points;
+
+
+      for ( ; count > 0; count--, read++, write++ )
+      {
+        if ( *read & SW_FT_STROKE_TAG_ON )
+          *write = SW_FT_CURVE_TAG_ON;
+        else if ( *read & SW_FT_STROKE_TAG_CUBIC )
+          *write = SW_FT_CURVE_TAG_CUBIC;
+        else
+          *write = SW_FT_CURVE_TAG_CONIC;
+      }
+    }
+
+    /* copy contours */
+    {
+      SW_FT_UInt    count = border->num_points;
+      SW_FT_Byte*   tags  = border->tags;
+      SW_FT_Short*  write = outline->contours + outline->n_contours;
+      SW_FT_Short   idx   = (SW_FT_Short)outline->n_points;
+
+
+      for ( ; count > 0; count--, tags++, idx++ )
+      {
+        if ( *tags & SW_FT_STROKE_TAG_END )
+        {
+          *write++ = idx;
+          outline->n_contours++;
+        }
+      }
+    }
+
+    outline->n_points = (short)( outline->n_points + border->num_points );
+
+    assert( SW_FT_Outline_Check( outline ) == 0 );
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                           STROKER                             *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+#define SW_FT_SIDE_TO_ROTATE( s )   ( SW_FT_ANGLE_PI2 - (s) * SW_FT_ANGLE_PI )
+
+  typedef struct  SW_FT_StrokerRec_
+  {
+    SW_FT_Angle             angle_in;             /* direction into curr join */
+    SW_FT_Angle             angle_out;            /* direction out of join  */
+    SW_FT_Vector            center;               /* current position */
+    SW_FT_Fixed             line_length;          /* length of last lineto */
+    SW_FT_Bool              first_point;          /* is this the start? */
+    SW_FT_Bool              subpath_open;         /* is the subpath open? */
+    SW_FT_Angle             subpath_angle;        /* subpath start direction */
+    SW_FT_Vector            subpath_start;        /* subpath start position */
+    SW_FT_Fixed             subpath_line_length;  /* subpath start lineto len */
+    SW_FT_Bool              handle_wide_strokes;  /* use wide strokes logic? */
+
+    SW_FT_Stroker_LineCap   line_cap;
+    SW_FT_Stroker_LineJoin  line_join;
+    SW_FT_Stroker_LineJoin  line_join_saved;
+    SW_FT_Fixed             miter_limit;
+    SW_FT_Fixed             radius;
+
+    SW_FT_StrokeBorderRec   borders[2];
+  } SW_FT_StrokerRec;
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_New( SW_FT_Stroker  *astroker )
+  {
+    SW_FT_Error    error = 0;           /* assigned in SW_FT_NEW */
+    SW_FT_Stroker  stroker = NULL;
+
+
+    stroker = (SW_FT_StrokerRec *) calloc(1, sizeof(SW_FT_StrokerRec));
+    if ( stroker )
+    {
+
+      ft_stroke_border_init( &stroker->borders[0]);
+      ft_stroke_border_init( &stroker->borders[1]);
+    }
+
+    *astroker = stroker;
+
+    return error;
+  }
+
+  void
+  SW_FT_Stroker_Rewind( SW_FT_Stroker  stroker )
+  {
+    if ( stroker )
+    {
+      ft_stroke_border_reset( &stroker->borders[0] );
+      ft_stroke_border_reset( &stroker->borders[1] );
+    }
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  void
+  SW_FT_Stroker_Set( SW_FT_Stroker           stroker,
+                  SW_FT_Fixed             radius,
+                  SW_FT_Stroker_LineCap   line_cap,
+                  SW_FT_Stroker_LineJoin  line_join,
+                  SW_FT_Fixed             miter_limit )
+  {
+    stroker->radius      = radius;
+    stroker->line_cap    = line_cap;
+    stroker->line_join   = line_join;
+    stroker->miter_limit = miter_limit;
+
+    /* ensure miter limit has sensible value */
+    if ( stroker->miter_limit < 0x10000 )
+      stroker->miter_limit = 0x10000;
+
+    /* save line join style:                                           */
+    /* line join style can be temporarily changed when stroking curves */
+    stroker->line_join_saved = line_join;
+
+    SW_FT_Stroker_Rewind( stroker );
+  }
+
+  /* documentation is in ftstroke.h */
+
+  void
+  SW_FT_Stroker_Done( SW_FT_Stroker  stroker )
+  {
+    if ( stroker )
+    {
+
+      ft_stroke_border_done( &stroker->borders[0] );
+      ft_stroke_border_done( &stroker->borders[1] );
+
+      free( stroker );
+    }
+  }
+
+
+  /* create a circular arc at a corner or cap */
+  static SW_FT_Error
+  ft_stroker_arcto( SW_FT_Stroker  stroker,
+                    SW_FT_Int      side )
+  {
+    SW_FT_Angle         total, rotate;
+    SW_FT_Fixed         radius = stroker->radius;
+    SW_FT_Error         error  = 0;
+    SW_FT_StrokeBorder  border = stroker->borders + side;
+
+
+    rotate = SW_FT_SIDE_TO_ROTATE( side );
+
+    total = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+    if ( total == SW_FT_ANGLE_PI )
+      total = -rotate * 2;
+
+    error = ft_stroke_border_arcto( border,
+                                    &stroker->center,
+                                    radius,
+                                    stroker->angle_in + rotate,
+                                    total );
+    border->movable = FALSE;
+    return error;
+  }
+
+
+  /* add a cap at the end of an opened path */
+  static SW_FT_Error
+  ft_stroker_cap( SW_FT_Stroker  stroker,
+                  SW_FT_Angle    angle,
+                  SW_FT_Int      side )
+  {
+    SW_FT_Error  error = 0;
+
+
+    if ( stroker->line_cap == SW_FT_STROKER_LINECAP_ROUND )
+    {
+      /* add a round cap */
+      stroker->angle_in  = angle;
+      stroker->angle_out = angle + SW_FT_ANGLE_PI;
+
+      error = ft_stroker_arcto( stroker, side );
+    }
+    else if ( stroker->line_cap == SW_FT_STROKER_LINECAP_SQUARE )
+    {
+      /* add a square cap */
+      SW_FT_Vector        delta, delta2;
+      SW_FT_Angle         rotate = SW_FT_SIDE_TO_ROTATE( side );
+      SW_FT_Fixed         radius = stroker->radius;
+      SW_FT_StrokeBorder  border = stroker->borders + side;
+
+
+      SW_FT_Vector_From_Polar( &delta2, radius, angle + rotate );
+      SW_FT_Vector_From_Polar( &delta,  radius, angle );
+
+      delta.x += stroker->center.x + delta2.x;
+      delta.y += stroker->center.y + delta2.y;
+
+      error = ft_stroke_border_lineto( border, &delta, FALSE );
+      if ( error )
+        goto Exit;
+
+      SW_FT_Vector_From_Polar( &delta2, radius, angle - rotate );
+      SW_FT_Vector_From_Polar( &delta,  radius, angle );
+
+      delta.x += delta2.x + stroker->center.x;
+      delta.y += delta2.y + stroker->center.y;
+
+      error = ft_stroke_border_lineto( border, &delta, FALSE );
+    }
+    else if ( stroker->line_cap == SW_FT_STROKER_LINECAP_BUTT )
+    {
+      /* add a butt ending */
+      SW_FT_Vector        delta;
+      SW_FT_Angle         rotate = SW_FT_SIDE_TO_ROTATE( side );
+      SW_FT_Fixed         radius = stroker->radius;
+      SW_FT_StrokeBorder  border = stroker->borders + side;
+
+
+      SW_FT_Vector_From_Polar( &delta, radius, angle + rotate );
+
+      delta.x += stroker->center.x;
+      delta.y += stroker->center.y;
+
+      error = ft_stroke_border_lineto( border, &delta, FALSE );
+      if ( error )
+        goto Exit;
+
+      SW_FT_Vector_From_Polar( &delta, radius, angle - rotate );
+
+      delta.x += stroker->center.x;
+      delta.y += stroker->center.y;
+
+      error = ft_stroke_border_lineto( border, &delta, FALSE );
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /* process an inside corner, i.e. compute intersection */
+  static SW_FT_Error
+  ft_stroker_inside( SW_FT_Stroker  stroker,
+                     SW_FT_Int      side,
+                     SW_FT_Fixed    line_length )
+  {
+    SW_FT_StrokeBorder  border = stroker->borders + side;
+    SW_FT_Angle         phi, theta, rotate;
+    SW_FT_Fixed         length, thcos;
+    SW_FT_Vector        delta;
+    SW_FT_Error         error = 0;
+    SW_FT_Bool          intersect;          /* use intersection of lines? */
+
+
+    rotate = SW_FT_SIDE_TO_ROTATE( side );
+
+    theta = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2;
+
+    /* Only intersect borders if between two lineto's and both */
+    /* lines are long enough (line_length is zero for curves). */
+    if ( !border->movable || line_length == 0 )
+      intersect = FALSE;
+    else
+    {
+      /* compute minimum required length of lines */
+      SW_FT_Fixed  min_length = ft_pos_abs( SW_FT_MulFix( stroker->radius,
+                                                    SW_FT_Tan( theta ) ) );
+
+
+      intersect = SW_FT_BOOL( stroker->line_length >= min_length &&
+                           line_length          >= min_length );
+    }
+
+    if ( !intersect )
+    {
+      SW_FT_Vector_From_Polar( &delta, stroker->radius,
+                            stroker->angle_out + rotate );
+      delta.x += stroker->center.x;
+      delta.y += stroker->center.y;
+
+      border->movable = FALSE;
+    }
+    else
+    {
+      /* compute median angle */
+      phi = stroker->angle_in + theta;
+
+      thcos = SW_FT_Cos( theta );
+
+      length = SW_FT_DivFix( stroker->radius, thcos );
+
+      SW_FT_Vector_From_Polar( &delta, length, phi + rotate );
+      delta.x += stroker->center.x;
+      delta.y += stroker->center.y;
+    }
+
+    error = ft_stroke_border_lineto( border, &delta, FALSE );
+
+    return error;
+  }
+
+
+  /* process an outside corner, i.e. compute bevel/miter/round */
+  static SW_FT_Error
+  ft_stroker_outside( SW_FT_Stroker  stroker,
+                      SW_FT_Int      side,
+                      SW_FT_Fixed    line_length )
+  {
+    SW_FT_StrokeBorder  border = stroker->borders + side;
+    SW_FT_Error         error;
+    SW_FT_Angle         rotate;
+
+
+    if ( stroker->line_join == SW_FT_STROKER_LINEJOIN_ROUND )
+      error = ft_stroker_arcto( stroker, side );
+    else
+    {
+      /* this is a mitered (pointed) or beveled (truncated) corner */
+      SW_FT_Fixed  sigma = 0, radius = stroker->radius;
+      SW_FT_Angle  theta = 0, phi = 0;
+      SW_FT_Fixed  thcos = 0;
+      SW_FT_Bool   bevel, fixed_bevel;
+
+
+      rotate = SW_FT_SIDE_TO_ROTATE( side );
+
+      bevel =
+        SW_FT_BOOL( stroker->line_join == SW_FT_STROKER_LINEJOIN_BEVEL );
+
+      fixed_bevel =
+        SW_FT_BOOL( stroker->line_join != SW_FT_STROKER_LINEJOIN_MITER_VARIABLE );
+
+      if ( !bevel )
+      {
+        theta = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+
+        if ( theta == SW_FT_ANGLE_PI )
+        {
+          theta = rotate;
+          phi   = stroker->angle_in;
+        }
+        else
+        {
+          theta /= 2;
+          phi    = stroker->angle_in + theta + rotate;
+        }
+
+        thcos = SW_FT_Cos( theta );
+        sigma = SW_FT_MulFix( stroker->miter_limit, thcos );
+
+        /* is miter limit exceeded? */
+        if ( sigma < 0x10000L )
+        {
+          /* don't create variable bevels for very small deviations; */
+          /* SW_FT_Sin(x) = 0 for x <= 57                               */
+          if ( fixed_bevel || ft_pos_abs( theta ) > 57 )
+            bevel = TRUE;
+        }
+      }
+
+      if ( bevel )  /* this is a bevel (broken angle) */
+      {
+        if ( fixed_bevel )
+        {
+          /* the outer corners are simply joined together */
+          SW_FT_Vector  delta;
+
+
+          /* add bevel */
+          SW_FT_Vector_From_Polar( &delta,
+                                radius,
+                                stroker->angle_out + rotate );
+          delta.x += stroker->center.x;
+          delta.y += stroker->center.y;
+
+          border->movable = FALSE;
+          error = ft_stroke_border_lineto( border, &delta, FALSE );
+        }
+        else /* variable bevel */
+        {
+          /* the miter is truncated */
+          SW_FT_Vector  middle, delta;
+          SW_FT_Fixed   length;
+
+
+          /* compute middle point */
+          SW_FT_Vector_From_Polar( &middle,
+                                SW_FT_MulFix( radius, stroker->miter_limit ),
+                                phi );
+          middle.x += stroker->center.x;
+          middle.y += stroker->center.y;
+
+          /* compute first angle point */
+          length = SW_FT_MulDiv( radius, 0x10000L - sigma,
+                              ft_pos_abs( SW_FT_Sin( theta ) ) );
+
+          SW_FT_Vector_From_Polar( &delta, length, phi + rotate );
+          delta.x += middle.x;
+          delta.y += middle.y;
+
+          error = ft_stroke_border_lineto( border, &delta, FALSE );
+          if ( error )
+            goto Exit;
+
+          /* compute second angle point */
+          SW_FT_Vector_From_Polar( &delta, length, phi - rotate );
+          delta.x += middle.x;
+          delta.y += middle.y;
+
+          error = ft_stroke_border_lineto( border, &delta, FALSE );
+          if ( error )
+            goto Exit;
+
+          /* finally, add an end point; only needed if not lineto */
+          /* (line_length is zero for curves)                     */
+          if ( line_length == 0 )
+          {
+            SW_FT_Vector_From_Polar( &delta,
+                                  radius,
+                                  stroker->angle_out + rotate );
+
+            delta.x += stroker->center.x;
+            delta.y += stroker->center.y;
+
+            error = ft_stroke_border_lineto( border, &delta, FALSE );
+          }
+        }
+      }
+      else /* this is a miter (intersection) */
+      {
+        SW_FT_Fixed   length;
+        SW_FT_Vector  delta;
+
+
+        length = SW_FT_DivFix( stroker->radius, thcos );
+
+        SW_FT_Vector_From_Polar( &delta, length, phi );
+        delta.x += stroker->center.x;
+        delta.y += stroker->center.y;
+
+        error = ft_stroke_border_lineto( border, &delta, FALSE );
+        if ( error )
+          goto Exit;
+
+        /* now add an end point; only needed if not lineto */
+        /* (line_length is zero for curves)                */
+        if ( line_length == 0 )
+        {
+          SW_FT_Vector_From_Polar( &delta,
+                                stroker->radius,
+                                stroker->angle_out + rotate );
+          delta.x += stroker->center.x;
+          delta.y += stroker->center.y;
+
+          error = ft_stroke_border_lineto( border, &delta, FALSE );
+        }
+      }
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  static SW_FT_Error
+  ft_stroker_process_corner( SW_FT_Stroker  stroker,
+                             SW_FT_Fixed    line_length )
+  {
+    SW_FT_Error  error = 0;
+    SW_FT_Angle  turn;
+    SW_FT_Int    inside_side;
+
+
+    turn = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+
+    /* no specific corner processing is required if the turn is 0 */
+    if ( turn == 0 )
+      goto Exit;
+
+    /* when we turn to the right, the inside side is 0 */
+    inside_side = 0;
+
+    /* otherwise, the inside side is 1 */
+    if ( turn < 0 )
+      inside_side = 1;
+
+    /* process the inside side */
+    error = ft_stroker_inside( stroker, inside_side, line_length );
+    if ( error )
+      goto Exit;
+
+    /* process the outside side */
+    error = ft_stroker_outside( stroker, 1 - inside_side, line_length );
+
+  Exit:
+    return error;
+  }
+
+
+  /* add two points to the left and right borders corresponding to the */
+  /* start of the subpath                                              */
+  static SW_FT_Error
+  ft_stroker_subpath_start( SW_FT_Stroker  stroker,
+                            SW_FT_Angle    start_angle,
+                            SW_FT_Fixed    line_length )
+  {
+    SW_FT_Vector        delta;
+    SW_FT_Vector        point;
+    SW_FT_Error         error;
+    SW_FT_StrokeBorder  border;
+
+
+    SW_FT_Vector_From_Polar( &delta, stroker->radius,
+                          start_angle + SW_FT_ANGLE_PI2 );
+
+    point.x = stroker->center.x + delta.x;
+    point.y = stroker->center.y + delta.y;
+
+    border = stroker->borders;
+    error = ft_stroke_border_moveto( border, &point );
+    if ( error )
+      goto Exit;
+
+    point.x = stroker->center.x - delta.x;
+    point.y = stroker->center.y - delta.y;
+
+    border++;
+    error = ft_stroke_border_moveto( border, &point );
+
+    /* save angle, position, and line length for last join */
+    /* (line_length is zero for curves)                    */
+    stroker->subpath_angle       = start_angle;
+    stroker->first_point         = FALSE;
+    stroker->subpath_line_length = line_length;
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_LineTo( SW_FT_Stroker  stroker,
+                     SW_FT_Vector*  to )
+  {
+    SW_FT_Error         error = 0;
+    SW_FT_StrokeBorder  border;
+    SW_FT_Vector        delta;
+    SW_FT_Angle         angle;
+    SW_FT_Int           side;
+    SW_FT_Fixed         line_length;
+
+
+    delta.x = to->x - stroker->center.x;
+    delta.y = to->y - stroker->center.y;
+
+    /* a zero-length lineto is a no-op; avoid creating a spurious corner */
+    if ( delta.x == 0 && delta.y == 0 )
+       goto Exit;
+
+    /* compute length of line */
+    line_length = SW_FT_Vector_Length( &delta );
+
+    angle = SW_FT_Atan2( delta.x, delta.y );
+    SW_FT_Vector_From_Polar( &delta, stroker->radius, angle + SW_FT_ANGLE_PI2 );
+
+    /* process corner if necessary */
+    if ( stroker->first_point )
+    {
+      /* This is the first segment of a subpath.  We need to     */
+      /* add a point to each border at their respective starting */
+      /* point locations.                                        */
+      error = ft_stroker_subpath_start( stroker, angle, line_length );
+      if ( error )
+        goto Exit;
+    }
+    else
+    {
+      /* process the current corner */
+      stroker->angle_out = angle;
+      error = ft_stroker_process_corner( stroker, line_length );
+      if ( error )
+        goto Exit;
+    }
+
+    /* now add a line segment to both the `inside' and `outside' paths */
+    for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
+    {
+      SW_FT_Vector  point;
+
+
+      point.x = to->x + delta.x;
+      point.y = to->y + delta.y;
+
+      /* the ends of lineto borders are movable */
+      error = ft_stroke_border_lineto( border, &point, TRUE );
+      if ( error )
+        goto Exit;
+
+      delta.x = -delta.x;
+      delta.y = -delta.y;
+    }
+
+    stroker->angle_in    = angle;
+    stroker->center      = *to;
+    stroker->line_length = line_length;
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_ConicTo( SW_FT_Stroker  stroker,
+                      SW_FT_Vector*  control,
+                      SW_FT_Vector*  to )
+  {
+    SW_FT_Error    error = 0;
+    SW_FT_Vector   bez_stack[34];
+    SW_FT_Vector*  arc;
+    SW_FT_Vector*  limit = bez_stack + 30;
+    SW_FT_Bool     first_arc = TRUE;
+
+
+    /* if all control points are coincident, this is a no-op; */
+    /* avoid creating a spurious corner                       */
+    if ( SW_FT_IS_SMALL( stroker->center.x - control->x ) &&
+         SW_FT_IS_SMALL( stroker->center.y - control->y ) &&
+         SW_FT_IS_SMALL( control->x        - to->x      ) &&
+         SW_FT_IS_SMALL( control->y        - to->y      ) )
+    {
+       stroker->center = *to;
+       goto Exit;
+    }
+
+    arc    = bez_stack;
+    arc[0] = *to;
+    arc[1] = *control;
+    arc[2] = stroker->center;
+
+    while ( arc >= bez_stack )
+    {
+      SW_FT_Angle  angle_in, angle_out;
+
+
+      /* initialize with current direction */
+      angle_in = angle_out = stroker->angle_in;
+
+      if ( arc < limit                                             &&
+           !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
+      {
+        if ( stroker->first_point )
+          stroker->angle_in = angle_in;
+
+        ft_conic_split( arc );
+        arc += 2;
+        continue;
+      }
+
+      if ( first_arc )
+      {
+        first_arc = FALSE;
+
+        /* process corner if necessary */
+        if ( stroker->first_point )
+          error = ft_stroker_subpath_start( stroker, angle_in, 0 );
+        else
+        {
+          stroker->angle_out = angle_in;
+          error = ft_stroker_process_corner( stroker, 0 );
+        }
+      }
+      else if ( ft_pos_abs( SW_FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
+                  SW_FT_SMALL_CONIC_THRESHOLD / 4                             )
+      {
+        /* if the deviation from one arc to the next is too great, */
+        /* add a round corner                                      */
+        stroker->center    = arc[2];
+        stroker->angle_out = angle_in;
+        stroker->line_join = SW_FT_STROKER_LINEJOIN_ROUND;
+
+        error = ft_stroker_process_corner( stroker, 0 );
+
+        /* reinstate line join style */
+        stroker->line_join = stroker->line_join_saved;
+      }
+
+      if ( error )
+        goto Exit;
+
+      /* the arc's angle is small enough; we can add it directly to each */
+      /* border                                                          */
+      {
+        SW_FT_Vector        ctrl, end;
+        SW_FT_Angle         theta, phi, rotate, alpha0 = 0;
+        SW_FT_Fixed         length;
+        SW_FT_StrokeBorder  border;
+        SW_FT_Int           side;
+
+
+        theta  = SW_FT_Angle_Diff( angle_in, angle_out ) / 2;
+        phi    = angle_in + theta;
+        length = SW_FT_DivFix( stroker->radius, SW_FT_Cos( theta ) );
+
+        /* compute direction of original arc */
+        if ( stroker->handle_wide_strokes )
+          alpha0 = SW_FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y );
+
+        for ( border = stroker->borders, side = 0;
+              side <= 1;
+              side++, border++ )
+        {
+          rotate = SW_FT_SIDE_TO_ROTATE( side );
+
+          /* compute control point */
+          SW_FT_Vector_From_Polar( &ctrl, length, phi + rotate );
+          ctrl.x += arc[1].x;
+          ctrl.y += arc[1].y;
+
+          /* compute end point */
+          SW_FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+          end.x += arc[0].x;
+          end.y += arc[0].y;
+
+          if ( stroker->handle_wide_strokes )
+          {
+            SW_FT_Vector  start;
+            SW_FT_Angle   alpha1;
+
+
+            /* determine whether the border radius is greater than the */
+            /* radius of curvature of the original arc                 */
+            start = border->points[border->num_points - 1];
+
+            alpha1 = SW_FT_Atan2( end.x - start.x, end.y - start.y );
+
+            /* is the direction of the border arc opposite to */
+            /* that of the original arc? */
+            if ( ft_pos_abs( SW_FT_Angle_Diff( alpha0, alpha1 ) ) >
+                   SW_FT_ANGLE_PI / 2                             )
+            {
+              SW_FT_Angle   beta, gamma;
+              SW_FT_Vector  bvec, delta;
+              SW_FT_Fixed   blen, sinA, sinB, alen;
+
+
+              /* use the sine rule to find the intersection point */
+              beta  = SW_FT_Atan2( arc[2].x - start.x, arc[2].y - start.y );
+              gamma = SW_FT_Atan2( arc[0].x - end.x,   arc[0].y - end.y );
+
+              bvec.x = end.x - start.x;
+              bvec.y = end.y - start.y;
+
+              blen = SW_FT_Vector_Length( &bvec );
+
+              sinA = ft_pos_abs( SW_FT_Sin( alpha1 - gamma ) );
+              sinB = ft_pos_abs( SW_FT_Sin( beta - gamma ) );
+
+              alen = SW_FT_MulDiv( blen, sinA, sinB );
+
+              SW_FT_Vector_From_Polar( &delta, alen, beta );
+              delta.x += start.x;
+              delta.y += start.y;
+
+              /* circumnavigate the negative sector backwards */
+              border->movable = FALSE;
+              error = ft_stroke_border_lineto( border, &delta, FALSE );
+              if ( error )
+                goto Exit;
+              error = ft_stroke_border_lineto( border, &end, FALSE );
+              if ( error )
+                goto Exit;
+              error = ft_stroke_border_conicto( border, &ctrl, &start );
+              if ( error )
+                goto Exit;
+              /* and then move to the endpoint */
+              error = ft_stroke_border_lineto( border, &end, FALSE );
+              if ( error )
+                goto Exit;
+
+              continue;
+            }
+
+            /* else fall through */
+          }
+
+          /* simply add an arc */
+          error = ft_stroke_border_conicto( border, &ctrl, &end );
+          if ( error )
+            goto Exit;
+        }
+      }
+
+      arc -= 2;
+
+      stroker->angle_in = angle_out;
+    }
+
+    stroker->center = *to;
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_CubicTo( SW_FT_Stroker  stroker,
+                      SW_FT_Vector*  control1,
+                      SW_FT_Vector*  control2,
+                      SW_FT_Vector*  to )
+  {
+    SW_FT_Error    error = 0;
+    SW_FT_Vector   bez_stack[37];
+    SW_FT_Vector*  arc;
+    SW_FT_Vector*  limit = bez_stack + 32;
+    SW_FT_Bool     first_arc = TRUE;
+
+
+    /* if all control points are coincident, this is a no-op; */
+    /* avoid creating a spurious corner */
+    if ( SW_FT_IS_SMALL( stroker->center.x - control1->x ) &&
+         SW_FT_IS_SMALL( stroker->center.y - control1->y ) &&
+         SW_FT_IS_SMALL( control1->x       - control2->x ) &&
+         SW_FT_IS_SMALL( control1->y       - control2->y ) &&
+         SW_FT_IS_SMALL( control2->x       - to->x       ) &&
+         SW_FT_IS_SMALL( control2->y       - to->y       ) )
+    {
+       stroker->center = *to;
+       goto Exit;
+    }
+
+    arc    = bez_stack;
+    arc[0] = *to;
+    arc[1] = *control2;
+    arc[2] = *control1;
+    arc[3] = stroker->center;
+
+    while ( arc >= bez_stack )
+    {
+      SW_FT_Angle  angle_in, angle_mid, angle_out;
+
+
+      /* initialize with current direction */
+      angle_in = angle_out = angle_mid = stroker->angle_in;
+
+      if ( arc < limit                                         &&
+           !ft_cubic_is_small_enough( arc, &angle_in,
+                                      &angle_mid, &angle_out ) )
+      {
+        if ( stroker->first_point )
+          stroker->angle_in = angle_in;
+
+        ft_cubic_split( arc );
+        arc += 3;
+        continue;
+      }
+
+      if ( first_arc )
+      {
+        first_arc = FALSE;
+
+        /* process corner if necessary */
+        if ( stroker->first_point )
+          error = ft_stroker_subpath_start( stroker, angle_in, 0 );
+        else
+        {
+          stroker->angle_out = angle_in;
+          error = ft_stroker_process_corner( stroker, 0 );
+        }
+      }
+      else if ( ft_pos_abs( SW_FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
+                  SW_FT_SMALL_CUBIC_THRESHOLD / 4                             )
+      {
+        /* if the deviation from one arc to the next is too great, */
+        /* add a round corner                                      */
+        stroker->center    = arc[3];
+        stroker->angle_out = angle_in;
+        stroker->line_join = SW_FT_STROKER_LINEJOIN_ROUND;
+
+        error = ft_stroker_process_corner( stroker, 0 );
+
+        /* reinstate line join style */
+        stroker->line_join = stroker->line_join_saved;
+      }
+
+      if ( error )
+        goto Exit;
+
+      /* the arc's angle is small enough; we can add it directly to each */
+      /* border                                                          */
+      {
+        SW_FT_Vector        ctrl1, ctrl2, end;
+        SW_FT_Angle         theta1, phi1, theta2, phi2, rotate, alpha0 = 0;
+        SW_FT_Fixed         length1, length2;
+        SW_FT_StrokeBorder  border;
+        SW_FT_Int           side;
+
+
+        theta1  = SW_FT_Angle_Diff( angle_in,  angle_mid ) / 2;
+        theta2  = SW_FT_Angle_Diff( angle_mid, angle_out ) / 2;
+        phi1    = ft_angle_mean( angle_in,  angle_mid );
+        phi2    = ft_angle_mean( angle_mid, angle_out );
+        length1 = SW_FT_DivFix( stroker->radius, SW_FT_Cos( theta1 ) );
+        length2 = SW_FT_DivFix( stroker->radius, SW_FT_Cos( theta2 ) );
+
+        /* compute direction of original arc */
+        if ( stroker->handle_wide_strokes )
+          alpha0 = SW_FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y );
+
+        for ( border = stroker->borders, side = 0;
+              side <= 1;
+              side++, border++ )
+        {
+          rotate = SW_FT_SIDE_TO_ROTATE( side );
+
+          /* compute control points */
+          SW_FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
+          ctrl1.x += arc[2].x;
+          ctrl1.y += arc[2].y;
+
+          SW_FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
+          ctrl2.x += arc[1].x;
+          ctrl2.y += arc[1].y;
+
+          /* compute end point */
+          SW_FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+          end.x += arc[0].x;
+          end.y += arc[0].y;
+
+          if ( stroker->handle_wide_strokes )
+          {
+            SW_FT_Vector  start;
+            SW_FT_Angle   alpha1;
+
+
+            /* determine whether the border radius is greater than the */
+            /* radius of curvature of the original arc                 */
+            start = border->points[border->num_points - 1];
+
+            alpha1 = SW_FT_Atan2( end.x - start.x, end.y - start.y );
+
+            /* is the direction of the border arc opposite to */
+            /* that of the original arc? */
+            if ( ft_pos_abs( SW_FT_Angle_Diff( alpha0, alpha1 ) ) >
+                   SW_FT_ANGLE_PI / 2                             )
+            {
+              SW_FT_Angle   beta, gamma;
+              SW_FT_Vector  bvec, delta;
+              SW_FT_Fixed   blen, sinA, sinB, alen;
+
+
+              /* use the sine rule to find the intersection point */
+              beta  = SW_FT_Atan2( arc[3].x - start.x, arc[3].y - start.y );
+              gamma = SW_FT_Atan2( arc[0].x - end.x,   arc[0].y - end.y );
+
+              bvec.x = end.x - start.x;
+              bvec.y = end.y - start.y;
+
+              blen = SW_FT_Vector_Length( &bvec );
+
+              sinA = ft_pos_abs( SW_FT_Sin( alpha1 - gamma ) );
+              sinB = ft_pos_abs( SW_FT_Sin( beta - gamma ) );
+
+              alen = SW_FT_MulDiv( blen, sinA, sinB );
+
+              SW_FT_Vector_From_Polar( &delta, alen, beta );
+              delta.x += start.x;
+              delta.y += start.y;
+
+              /* circumnavigate the negative sector backwards */
+              border->movable = FALSE;
+              error = ft_stroke_border_lineto( border, &delta, FALSE );
+              if ( error )
+                goto Exit;
+              error = ft_stroke_border_lineto( border, &end, FALSE );
+              if ( error )
+                goto Exit;
+              error = ft_stroke_border_cubicto( border,
+                                                &ctrl2,
+                                                &ctrl1,
+                                                &start );
+              if ( error )
+                goto Exit;
+              /* and then move to the endpoint */
+              error = ft_stroke_border_lineto( border, &end, FALSE );
+              if ( error )
+                goto Exit;
+
+              continue;
+            }
+
+            /* else fall through */
+          }
+
+          /* simply add an arc */
+          error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end );
+          if ( error )
+            goto Exit;
+        }
+      }
+
+      arc -= 3;
+
+      stroker->angle_in = angle_out;
+    }
+
+    stroker->center = *to;
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_BeginSubPath( SW_FT_Stroker  stroker,
+                           SW_FT_Vector*  to,
+                           SW_FT_Bool     open )
+  {
+    /* We cannot process the first point, because there is not enough      */
+    /* information regarding its corner/cap.  The latter will be processed */
+    /* in the `SW_FT_Stroker_EndSubPath' routine.                             */
+    /*                                                                     */
+    stroker->first_point  = TRUE;
+    stroker->center       = *to;
+    stroker->subpath_open = open;
+
+    /* Determine if we need to check whether the border radius is greater */
+    /* than the radius of curvature of a curve, to handle this case       */
+    /* specially.  This is only required if bevel joins or butt caps may  */
+    /* be created, because round & miter joins and round & square caps    */
+    /* cover the negative sector created with wide strokes.               */
+    stroker->handle_wide_strokes =
+      SW_FT_BOOL( stroker->line_join != SW_FT_STROKER_LINEJOIN_ROUND  ||
+               ( stroker->subpath_open                        &&
+                 stroker->line_cap == SW_FT_STROKER_LINECAP_BUTT ) );
+
+    /* record the subpath start point for each border */
+    stroker->subpath_start = *to;
+
+    stroker->angle_in = 0;
+
+    return 0;
+  }
+
+
+  static SW_FT_Error
+  ft_stroker_add_reverse_left( SW_FT_Stroker  stroker,
+                               SW_FT_Bool     open )
+  {
+    SW_FT_StrokeBorder  right = stroker->borders + 0;
+    SW_FT_StrokeBorder  left  = stroker->borders + 1;
+    SW_FT_Int           new_points;
+    SW_FT_Error         error = 0;
+
+
+    assert( left->start >= 0 );
+
+    new_points = left->num_points - left->start;
+    if ( new_points > 0 )
+    {
+      error = ft_stroke_border_grow( right, (SW_FT_UInt)new_points );
+      if ( error )
+        goto Exit;
+
+      {
+        SW_FT_Vector*  dst_point = right->points + right->num_points;
+        SW_FT_Byte*    dst_tag   = right->tags   + right->num_points;
+        SW_FT_Vector*  src_point = left->points  + left->num_points - 1;
+        SW_FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
+
+
+        while ( src_point >= left->points + left->start )
+        {
+          *dst_point = *src_point;
+          *dst_tag   = *src_tag;
+
+          if ( open )
+            dst_tag[0] &= ~SW_FT_STROKE_TAG_BEGIN_END;
+          else
+          {
+            SW_FT_Byte  ttag =
+                       (SW_FT_Byte)( dst_tag[0] & SW_FT_STROKE_TAG_BEGIN_END );
+
+
+            /* switch begin/end tags if necessary */
+            if ( ttag == SW_FT_STROKE_TAG_BEGIN ||
+                 ttag == SW_FT_STROKE_TAG_END   )
+              dst_tag[0] ^= SW_FT_STROKE_TAG_BEGIN_END;
+          }
+
+          src_point--;
+          src_tag--;
+          dst_point++;
+          dst_tag++;
+        }
+      }
+
+      left->num_points   = left->start;
+      right->num_points += new_points;
+
+      right->movable = FALSE;
+      left->movable  = FALSE;
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  /* there's a lot of magic in this function! */
+  SW_FT_Error
+  SW_FT_Stroker_EndSubPath( SW_FT_Stroker  stroker )
+  {
+    SW_FT_Error  error = 0;
+
+
+    if ( stroker->subpath_open )
+    {
+      SW_FT_StrokeBorder  right = stroker->borders;
+
+
+      /* All right, this is an opened path, we need to add a cap between */
+      /* right & left, add the reverse of left, then add a final cap     */
+      /* between left & right.                                           */
+      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
+      if ( error )
+        goto Exit;
+
+      /* add reversed points from `left' to `right' */
+      error = ft_stroker_add_reverse_left( stroker, TRUE );
+      if ( error )
+        goto Exit;
+
+      /* now add the final cap */
+      stroker->center = stroker->subpath_start;
+      error = ft_stroker_cap( stroker,
+                              stroker->subpath_angle + SW_FT_ANGLE_PI, 0 );
+      if ( error )
+        goto Exit;
+
+      /* Now end the right subpath accordingly.  The left one is */
+      /* rewind and doesn't need further processing.             */
+      ft_stroke_border_close( right, FALSE );
+    }
+    else
+    {
+      SW_FT_Angle  turn;
+      SW_FT_Int    inside_side;
+
+
+      /* close the path if needed */
+      if ( stroker->center.x != stroker->subpath_start.x ||
+           stroker->center.y != stroker->subpath_start.y )
+      {
+         error = SW_FT_Stroker_LineTo( stroker, &stroker->subpath_start );
+         if ( error )
+           goto Exit;
+      }
+
+      /* process the corner */
+      stroker->angle_out = stroker->subpath_angle;
+      turn               = SW_FT_Angle_Diff( stroker->angle_in,
+                                          stroker->angle_out );
+
+      /* no specific corner processing is required if the turn is 0 */
+      if ( turn != 0 )
+      {
+        /* when we turn to the right, the inside side is 0 */
+        inside_side = 0;
+
+        /* otherwise, the inside side is 1 */
+        if ( turn < 0 )
+          inside_side = 1;
+
+        error = ft_stroker_inside( stroker,
+                                   inside_side,
+                                   stroker->subpath_line_length );
+        if ( error )
+          goto Exit;
+
+        /* process the outside side */
+        error = ft_stroker_outside( stroker,
+                                    1 - inside_side,
+                                    stroker->subpath_line_length );
+        if ( error )
+          goto Exit;
+      }
+
+      /* then end our two subpaths */
+      ft_stroke_border_close( stroker->borders + 0, FALSE );
+      ft_stroke_border_close( stroker->borders + 1, TRUE );
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_GetBorderCounts( SW_FT_Stroker        stroker,
+                              SW_FT_StrokerBorder  border,
+                              SW_FT_UInt          *anum_points,
+                              SW_FT_UInt          *anum_contours )
+  {
+    SW_FT_UInt   num_points = 0, num_contours = 0;
+    SW_FT_Error  error;
+
+
+    if ( !stroker || border > 1 )
+    {
+      error = -1;//SW_FT_THROW( Invalid_Argument );
+      goto Exit;
+    }
+
+    error = ft_stroke_border_get_counts( stroker->borders + border,
+                                         &num_points, &num_contours );
+  Exit:
+    if ( anum_points )
+      *anum_points = num_points;
+
+    if ( anum_contours )
+      *anum_contours = num_contours;
+
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  SW_FT_Error
+  SW_FT_Stroker_GetCounts( SW_FT_Stroker  stroker,
+                        SW_FT_UInt    *anum_points,
+                        SW_FT_UInt    *anum_contours )
+  {
+    SW_FT_UInt   count1, count2, num_points   = 0;
+    SW_FT_UInt   count3, count4, num_contours = 0;
+    SW_FT_Error  error;
+
+
+    error = ft_stroke_border_get_counts( stroker->borders + 0,
+                                         &count1, &count2 );
+    if ( error )
+      goto Exit;
+
+    error = ft_stroke_border_get_counts( stroker->borders + 1,
+                                         &count3, &count4 );
+    if ( error )
+      goto Exit;
+
+    num_points   = count1 + count3;
+    num_contours = count2 + count4;
+
+  Exit:
+    *anum_points   = num_points;
+    *anum_contours = num_contours;
+    return error;
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  void
+  SW_FT_Stroker_ExportBorder( SW_FT_Stroker        stroker,
+                           SW_FT_StrokerBorder  border,
+                           SW_FT_Outline*       outline )
+  {
+    if ( border == SW_FT_STROKER_BORDER_LEFT  ||
+         border == SW_FT_STROKER_BORDER_RIGHT )
+    {
+      SW_FT_StrokeBorder  sborder = & stroker->borders[border];
+
+
+      if ( sborder->valid )
+        ft_stroke_border_export( sborder, outline );
+    }
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  void
+  SW_FT_Stroker_Export( SW_FT_Stroker   stroker,
+                     SW_FT_Outline*  outline )
+  {
+    SW_FT_Stroker_ExportBorder( stroker, SW_FT_STROKER_BORDER_LEFT, outline );
+    SW_FT_Stroker_ExportBorder( stroker, SW_FT_STROKER_BORDER_RIGHT, outline );
+  }
+
+
+  /* documentation is in ftstroke.h */
+
+  /*
+   *  The following is very similar to SW_FT_Outline_Decompose, except
+   *  that we do support opened paths, and do not scale the outline.
+   */
+  SW_FT_Error
+  SW_FT_Stroker_ParseOutline( SW_FT_Stroker   stroker,
+                           const SW_FT_Outline*  outline,
+                           SW_FT_Bool      opened )
+  {
+    SW_FT_Vector   v_last;
+    SW_FT_Vector   v_control;
+    SW_FT_Vector   v_start;
+
+    SW_FT_Vector*  point;
+    SW_FT_Vector*  limit;
+    char*       tags;
+
+    SW_FT_Error    error;
+
+    SW_FT_Int      n;         /* index of contour in outline     */
+    SW_FT_UInt     first;     /* index of first point in contour */
+    SW_FT_Int      tag;       /* current point's state           */
+
+
+    if ( !outline || !stroker )
+      return -1;//SW_FT_THROW( Invalid_Argument );
+
+    SW_FT_Stroker_Rewind( stroker );
+
+    first = 0;
+
+    for ( n = 0; n < outline->n_contours; n++ )
+    {
+      SW_FT_UInt  last;  /* index of last point in contour */
+
+
+      last  = outline->contours[n];
+      limit = outline->points + last;
+
+      /* skip empty points; we don't stroke these */
+      if ( last <= first )
+      {
+        first = last + 1;
+        continue;
+      }
+
+      v_start = outline->points[first];
+      v_last  = outline->points[last];
+
+      v_control = v_start;
+
+      point = outline->points + first;
+      tags  = outline->tags   + first;
+      tag   = SW_FT_CURVE_TAG( tags[0] );
+
+      /* A contour cannot start with a cubic control point! */
+      if ( tag == SW_FT_CURVE_TAG_CUBIC )
+        goto Invalid_Outline;
+
+      /* check first point to determine origin */
+      if ( tag == SW_FT_CURVE_TAG_CONIC )
+      {
+        /* First point is conic control.  Yes, this happens. */
+        if ( SW_FT_CURVE_TAG( outline->tags[last] ) == SW_FT_CURVE_TAG_ON )
+        {
+          /* start at last point if it is on the curve */
+          v_start = v_last;
+          limit--;
+        }
+        else
+        {
+          /* if both first and last points are conic, */
+          /* start at their middle                    */
+          v_start.x = ( v_start.x + v_last.x ) / 2;
+          v_start.y = ( v_start.y + v_last.y ) / 2;
+        }
+        point--;
+        tags--;
+      }
+
+      error = SW_FT_Stroker_BeginSubPath( stroker, &v_start, opened );
+      if ( error )
+        goto Exit;
+
+      while ( point < limit )
+      {
+        point++;
+        tags++;
+
+        tag = SW_FT_CURVE_TAG( tags[0] );
+        switch ( tag )
+        {
+        case SW_FT_CURVE_TAG_ON:  /* emit a single line_to */
+          {
+            SW_FT_Vector  vec;
+
+
+            vec.x = point->x;
+            vec.y = point->y;
+
+            error = SW_FT_Stroker_LineTo( stroker, &vec );
+            if ( error )
+              goto Exit;
+            continue;
+          }
+
+        case SW_FT_CURVE_TAG_CONIC:  /* consume conic arcs */
+          v_control.x = point->x;
+          v_control.y = point->y;
+
+        Do_Conic:
+          if ( point < limit )
+          {
+            SW_FT_Vector  vec;
+            SW_FT_Vector  v_middle;
+
+
+            point++;
+            tags++;
+            tag = SW_FT_CURVE_TAG( tags[0] );
+
+            vec = point[0];
+
+            if ( tag == SW_FT_CURVE_TAG_ON )
+            {
+              error = SW_FT_Stroker_ConicTo( stroker, &v_control, &vec );
+              if ( error )
+                goto Exit;
+              continue;
+            }
+
+            if ( tag != SW_FT_CURVE_TAG_CONIC )
+              goto Invalid_Outline;
+
+            v_middle.x = ( v_control.x + vec.x ) / 2;
+            v_middle.y = ( v_control.y + vec.y ) / 2;
+
+            error = SW_FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
+            if ( error )
+              goto Exit;
+
+            v_control = vec;
+            goto Do_Conic;
+          }
+
+          error = SW_FT_Stroker_ConicTo( stroker, &v_control, &v_start );
+          goto Close;
+
+        default:  /* SW_FT_CURVE_TAG_CUBIC */
+          {
+            SW_FT_Vector  vec1, vec2;
+
+
+            if ( point + 1 > limit                             ||
+                 SW_FT_CURVE_TAG( tags[1] ) != SW_FT_CURVE_TAG_CUBIC )
+              goto Invalid_Outline;
+
+            point += 2;
+            tags  += 2;
+
+            vec1 = point[-2];
+            vec2 = point[-1];
+
+            if ( point <= limit )
+            {
+              SW_FT_Vector  vec;
+
+
+              vec = point[0];
+
+              error = SW_FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
+              if ( error )
+                goto Exit;
+              continue;
+            }
+
+            error = SW_FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
+            goto Close;
+          }
+        }
+      }
+
+    Close:
+      if ( error )
+        goto Exit;
+
+      /* don't try to end the path if no segments have been generated */
+      if ( !stroker->first_point )
+      {
+        error = SW_FT_Stroker_EndSubPath( stroker );
+        if ( error )
+          goto Exit;
+      }
+
+      first = last + 1;
+    }
+
+    return 0;
+
+  Exit:
+    return error;
+
+  Invalid_Outline:
+    return -2;//SW_FT_THROW( Invalid_Outline );
+  }
+
+
+/* END */
diff --git a/src/vector/freetype/v_ft_stroker.h b/src/vector/freetype/v_ft_stroker.h
new file mode 100644 (file)
index 0000000..d2b54e4
--- /dev/null
@@ -0,0 +1,325 @@
+#ifndef V_FT_STROKER_H
+#define V_FT_STROKER_H
+/***************************************************************************/
+/*                                                                         */
+/*  ftstroke.h                                                             */
+/*                                                                         */
+/*    FreeType path stroker (specification).                               */
+/*                                                                         */
+/*  Copyright 2002-2006, 2008, 2009, 2011-2012 by                          */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+#include "v_ft_raster.h"
+
+ /**************************************************************
+  *
+  * @type:
+  *   SW_FT_Stroker
+  *
+  * @description:
+  *   Opaque handler to a path stroker object.
+  */
+  typedef struct SW_FT_StrokerRec_*  SW_FT_Stroker;
+
+
+  /**************************************************************
+   *
+   * @enum:
+   *   SW_FT_Stroker_LineJoin
+   *
+   * @description:
+   *   These values determine how two joining lines are rendered
+   *   in a stroker.
+   *
+   * @values:
+   *   SW_FT_STROKER_LINEJOIN_ROUND ::
+   *     Used to render rounded line joins.  Circular arcs are used
+   *     to join two lines smoothly.
+   *
+   *   SW_FT_STROKER_LINEJOIN_BEVEL ::
+   *     Used to render beveled line joins.  The outer corner of
+   *     the joined lines is filled by enclosing the triangular
+   *     region of the corner with a straight line between the
+   *     outer corners of each stroke.
+   *
+   *   SW_FT_STROKER_LINEJOIN_MITER_FIXED ::
+   *     Used to render mitered line joins, with fixed bevels if the
+   *     miter limit is exceeded.  The outer edges of the strokes
+   *     for the two segments are extended until they meet at an
+   *     angle.  If the segments meet at too sharp an angle (such
+   *     that the miter would extend from the intersection of the
+   *     segments a distance greater than the product of the miter
+   *     limit value and the border radius), then a bevel join (see
+   *     above) is used instead.  This prevents long spikes being
+   *     created.  SW_FT_STROKER_LINEJOIN_MITER_FIXED generates a miter
+   *     line join as used in PostScript and PDF.
+   *
+   *   SW_FT_STROKER_LINEJOIN_MITER_VARIABLE ::
+   *   SW_FT_STROKER_LINEJOIN_MITER ::
+   *     Used to render mitered line joins, with variable bevels if
+   *     the miter limit is exceeded.  The intersection of the
+   *     strokes is clipped at a line perpendicular to the bisector
+   *     of the angle between the strokes, at the distance from the
+   *     intersection of the segments equal to the product of the
+   *     miter limit value and the border radius.  This prevents
+   *     long spikes being created.
+   *     SW_FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line
+   *     join as used in XPS.  SW_FT_STROKER_LINEJOIN_MITER is an alias
+   *     for SW_FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for
+   *     backwards compatibility.
+   */
+  typedef enum  SW_FT_Stroker_LineJoin_
+  {
+    SW_FT_STROKER_LINEJOIN_ROUND          = 0,
+    SW_FT_STROKER_LINEJOIN_BEVEL          = 1,
+    SW_FT_STROKER_LINEJOIN_MITER_VARIABLE = 2,
+    SW_FT_STROKER_LINEJOIN_MITER          = SW_FT_STROKER_LINEJOIN_MITER_VARIABLE,
+    SW_FT_STROKER_LINEJOIN_MITER_FIXED    = 3
+
+  } SW_FT_Stroker_LineJoin;
+
+
+  /**************************************************************
+   *
+   * @enum:
+   *   SW_FT_Stroker_LineCap
+   *
+   * @description:
+   *   These values determine how the end of opened sub-paths are
+   *   rendered in a stroke.
+   *
+   * @values:
+   *   SW_FT_STROKER_LINECAP_BUTT ::
+   *     The end of lines is rendered as a full stop on the last
+   *     point itself.
+   *
+   *   SW_FT_STROKER_LINECAP_ROUND ::
+   *     The end of lines is rendered as a half-circle around the
+   *     last point.
+   *
+   *   SW_FT_STROKER_LINECAP_SQUARE ::
+   *     The end of lines is rendered as a square around the
+   *     last point.
+   */
+  typedef enum  SW_FT_Stroker_LineCap_
+  {
+    SW_FT_STROKER_LINECAP_BUTT = 0,
+    SW_FT_STROKER_LINECAP_ROUND,
+    SW_FT_STROKER_LINECAP_SQUARE
+
+  } SW_FT_Stroker_LineCap;
+
+
+  /**************************************************************
+   *
+   * @enum:
+   *   SW_FT_StrokerBorder
+   *
+   * @description:
+   *   These values are used to select a given stroke border
+   *   in @SW_FT_Stroker_GetBorderCounts and @SW_FT_Stroker_ExportBorder.
+   *
+   * @values:
+   *   SW_FT_STROKER_BORDER_LEFT ::
+   *     Select the left border, relative to the drawing direction.
+   *
+   *   SW_FT_STROKER_BORDER_RIGHT ::
+   *     Select the right border, relative to the drawing direction.
+   *
+   * @note:
+   *   Applications are generally interested in the `inside' and `outside'
+   *   borders.  However, there is no direct mapping between these and the
+   *   `left' and `right' ones, since this really depends on the glyph's
+   *   drawing orientation, which varies between font formats.
+   *
+   *   You can however use @SW_FT_Outline_GetInsideBorder and
+   *   @SW_FT_Outline_GetOutsideBorder to get these.
+   */
+  typedef enum  SW_FT_StrokerBorder_
+  {
+    SW_FT_STROKER_BORDER_LEFT = 0,
+    SW_FT_STROKER_BORDER_RIGHT
+
+  } SW_FT_StrokerBorder;
+
+
+  /**************************************************************
+   *
+   * @function:
+   *   SW_FT_Stroker_New
+   *
+   * @description:
+   *   Create a new stroker object.
+   *
+   * @input:
+   *   library ::
+   *     FreeType library handle.
+   *
+   * @output:
+   *   astroker ::
+   *     A new stroker object handle.  NULL in case of error.
+   *
+   * @return:
+   *    FreeType error code.  0~means success.
+   */
+  SW_FT_Error
+  SW_FT_Stroker_New( SW_FT_Stroker  *astroker );
+
+
+  /**************************************************************
+   *
+   * @function:
+   *   SW_FT_Stroker_Set
+   *
+   * @description:
+   *   Reset a stroker object's attributes.
+   *
+   * @input:
+   *   stroker ::
+   *     The target stroker handle.
+   *
+   *   radius ::
+   *     The border radius.
+   *
+   *   line_cap ::
+   *     The line cap style.
+   *
+   *   line_join ::
+   *     The line join style.
+   *
+   *   miter_limit ::
+   *     The miter limit for the SW_FT_STROKER_LINEJOIN_MITER_FIXED and
+   *     SW_FT_STROKER_LINEJOIN_MITER_VARIABLE line join styles,
+   *     expressed as 16.16 fixed-point value.
+   *
+   * @note:
+   *   The radius is expressed in the same units as the outline
+   *   coordinates.
+   */
+  void
+  SW_FT_Stroker_Set( SW_FT_Stroker           stroker,
+                  SW_FT_Fixed             radius,
+                  SW_FT_Stroker_LineCap   line_cap,
+                  SW_FT_Stroker_LineJoin  line_join,
+                  SW_FT_Fixed             miter_limit );
+
+  /**************************************************************
+   *
+   * @function:
+   *   SW_FT_Stroker_ParseOutline
+   *
+   * @description:
+   *   A convenience function used to parse a whole outline with
+   *   the stroker.  The resulting outline(s) can be retrieved
+   *   later by functions like @SW_FT_Stroker_GetCounts and @SW_FT_Stroker_Export.
+   *
+   * @input:
+   *   stroker ::
+   *     The target stroker handle.
+   *
+   *   outline ::
+   *     The source outline.
+   *
+   *   opened ::
+   *     A boolean.  If~1, the outline is treated as an open path instead
+   *     of a closed one.
+   *
+   * @return:
+   *   FreeType error code.  0~means success.
+   *
+   * @note:
+   *   If `opened' is~0 (the default), the outline is treated as a closed
+   *   path, and the stroker generates two distinct `border' outlines.
+   *
+   *   If `opened' is~1, the outline is processed as an open path, and the
+   *   stroker generates a single `stroke' outline.
+   *
+   *   This function calls @SW_FT_Stroker_Rewind automatically.
+   */
+  SW_FT_Error
+  SW_FT_Stroker_ParseOutline( SW_FT_Stroker   stroker,
+                             const SW_FT_Outline*  outline,
+                             SW_FT_Bool      opened );
+
+
+  /**************************************************************
+   *
+   * @function:
+   *   SW_FT_Stroker_GetCounts
+   *
+   * @description:
+   *   Call this function once you have finished parsing your paths
+   *   with the stroker.  It returns the number of points and
+   *   contours necessary to export all points/borders from the stroked
+   *   outline/path.
+   *
+   * @input:
+   *   stroker ::
+   *     The target stroker handle.
+   *
+   * @output:
+   *   anum_points ::
+   *     The number of points.
+   *
+   *   anum_contours ::
+   *     The number of contours.
+   *
+   * @return:
+   *   FreeType error code.  0~means success.
+   */
+  SW_FT_Error
+  SW_FT_Stroker_GetCounts( SW_FT_Stroker  stroker,
+                        SW_FT_UInt    *anum_points,
+                        SW_FT_UInt    *anum_contours );
+
+
+  /**************************************************************
+   *
+   * @function:
+   *   SW_FT_Stroker_Export
+   *
+   * @description:
+   *   Call this function after @SW_FT_Stroker_GetBorderCounts to
+   *   export all borders to your own @SW_FT_Outline structure.
+   *
+   *   Note that this function appends the border points and
+   *   contours to your outline, but does not try to resize its
+   *   arrays.
+   *
+   * @input:
+   *   stroker ::
+   *     The target stroker handle.
+   *
+   *   outline ::
+   *     The target outline handle.
+   */
+  void
+  SW_FT_Stroker_Export( SW_FT_Stroker   stroker,
+                     SW_FT_Outline*  outline );
+
+
+  /**************************************************************
+   *
+   * @function:
+   *   SW_FT_Stroker_Done
+   *
+   * @description:
+   *   Destroy a stroker object.
+   *
+   * @input:
+   *   stroker ::
+   *     A stroker handle.  Can be NULL.
+   */
+  void
+  SW_FT_Stroker_Done( SW_FT_Stroker  stroker );
+
+
+#endif // V_FT_STROKER_H
diff --git a/src/vector/freetype/v_ft_types.h b/src/vector/freetype/v_ft_types.h
new file mode 100644 (file)
index 0000000..a01c4f2
--- /dev/null
@@ -0,0 +1,160 @@
+#ifndef V_FT_TYPES_H
+#define V_FT_TYPES_H
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Fixed                                                           */
+/*                                                                       */
+/* <Description>                                                         */
+/*    This type is used to store 16.16 fixed-point values, like scaling  */
+/*    values or matrix coefficients.                                     */
+/*                                                                       */
+typedef signed long  SW_FT_Fixed;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Int                                                             */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A typedef for the int type.                                        */
+/*                                                                       */
+typedef signed int  SW_FT_Int;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_UInt                                                            */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A typedef for the unsigned int type.                               */
+/*                                                                       */
+typedef unsigned int  SW_FT_UInt;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Long                                                            */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A typedef for signed long.                                         */
+/*                                                                       */
+typedef signed long  SW_FT_Long;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_ULong                                                           */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A typedef for unsigned long.                                       */
+/*                                                                       */
+typedef unsigned long SW_FT_ULong;
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Short                                                           */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A typedef for signed short.                                        */
+/*                                                                       */
+typedef signed short  SW_FT_Short;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Byte                                                            */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A simple typedef for the _unsigned_ char type.                     */
+/*                                                                       */
+typedef unsigned char  SW_FT_Byte;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Bool                                                            */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A typedef of unsigned char, used for simple booleans.  As usual,   */
+/*    values 1 and~0 represent true and false, respectively.             */
+/*                                                                       */
+typedef unsigned char  SW_FT_Bool;
+
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Error                                                           */
+/*                                                                       */
+/* <Description>                                                         */
+/*    The FreeType error code type.  A value of~0 is always interpreted  */
+/*    as a successful operation.                                         */
+/*                                                                       */
+typedef int  SW_FT_Error;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Type>                                                                */
+/*    SW_FT_Pos                                                             */
+/*                                                                       */
+/* <Description>                                                         */
+/*    The type SW_FT_Pos is used to store vectorial coordinates.  Depending */
+/*    on the context, these can represent distances in integer font      */
+/*    units, or 16.16, or 26.6 fixed-point pixel coordinates.            */
+/*                                                                       */
+typedef signed long  SW_FT_Pos;
+
+
+/*************************************************************************/
+/*                                                                       */
+/* <Struct>                                                              */
+/*    SW_FT_Vector                                                          */
+/*                                                                       */
+/* <Description>                                                         */
+/*    A simple structure used to store a 2D vector; coordinates are of   */
+/*    the SW_FT_Pos type.                                                   */
+/*                                                                       */
+/* <Fields>                                                              */
+/*    x :: The horizontal coordinate.                                    */
+/*    y :: The vertical coordinate.                                      */
+/*                                                                       */
+typedef struct  SW_FT_Vector_
+{
+  SW_FT_Pos  x;
+  SW_FT_Pos  y;
+
+} SW_FT_Vector;
+
+
+typedef long long int           SW_FT_Int64;
+typedef unsigned long long int  SW_FT_UInt64;
+
+typedef signed int              SW_FT_Int32;
+typedef unsigned int            SW_FT_UInt32;
+
+
+#define SW_FT_BOOL( x )  ( (SW_FT_Bool)( x ) )
+
+#define SW_FT_SIZEOF_LONG 4
+
+#ifndef TRUE
+#define TRUE  1
+#endif
+
+#ifndef FALSE
+#define FALSE  0
+#endif
+
+
+#endif // V_FT_TYPES_H
diff --git a/src/vector/meson.build b/src/vector/meson.build
new file mode 100644 (file)
index 0000000..761309a
--- /dev/null
@@ -0,0 +1,29 @@
+
+subdir('freetype')
+
+vector_dep = [freetype_dep]
+
+source_file   = files('vdasher.cpp')
+source_file  += files('vbrush.cpp')
+source_file  += files('vbitmap.cpp')
+source_file  += files('vpainter.cpp')
+source_file  += files('vcompositionfunctions.cpp')
+source_file  += files('vdrawhelper.cpp')
+source_file  += files('vdrawhelper_sse2.cpp')
+
+source_file  += files('vregion.cpp')
+source_file  += files('vrle.cpp')
+source_file  += files('vpath.cpp')
+source_file  += files('vpathmesure.cpp')
+source_file  += files('vmatrix.cpp')
+source_file  += files('velapsedtimer.cpp')
+source_file  += files('vdebug.cpp')
+source_file  += files('vinterpolator.cpp')
+source_file  += files('vbezier.cpp')
+source_file  += files('vraster.cpp')
+
+vector_dep += declare_dependency( include_directories : include_directories('.'),
+                                  sources             : source_file
+                                )
+
+
diff --git a/src/vector/vbezier.cpp b/src/vector/vbezier.cpp
new file mode 100644 (file)
index 0000000..3ae1e1d
--- /dev/null
@@ -0,0 +1,116 @@
+#include "vbezier.h"
+
+#include<cmath>
+
+// Approximate sqrt(x*x + y*y) using the alpha max plus beta min algorithm.
+// This uses alpha = 1, beta = 3/8, which results in a maximum error of less
+// than 7% compared to the correct value.
+static inline float
+lineLength(float x1, float y1, float x2, float y2)
+{
+   float x = x2 - x1;
+   float y = y2 - y1;
+
+   x = x < 0 ? -x : x;
+   y = y < 0 ? -y : y;
+
+   return (x > y ? x + 0.375 * y : y + 0.375 * x);
+}
+
+VBezier VBezier::fromPoints(const VPointF &p1, const VPointF &p2,
+                            const VPointF &p3, const VPointF &p4)
+{
+    VBezier b;
+    b.x1 = p1.x();
+    b.y1 = p1.y();
+    b.x2 = p2.x();
+    b.y2 = p2.y();
+    b.x3 = p3.x();
+    b.y3 = p3.y();
+    b.x4 = p4.x();
+    b.y4 = p4.y();
+    return b;
+}
+
+float
+VBezier::length()const
+{
+   VBezier left, right; /* bez poly splits */
+   float len = 0.0; /* arc length */
+   float chord; /* chord length */
+   float length;
+
+   len = len + lineLength(x1, y1, x2, y2);
+   len = len + lineLength(x2, y2, x3, y3);
+   len = len + lineLength(x3, y3, x4, y4);
+
+   chord = lineLength(x1, y1, x4, y4);
+
+   if (!floatCmp(len, chord)) {
+      split(&left, &right); /* split in two */
+      length =
+               left.length() + /* try left side */
+               right.length(); /* try right side */
+
+      return length;
+   }
+
+   return len;
+}
+
+VBezier VBezier::onInterval(float t0, float t1) const
+{
+    if (t0 == 0 && t1 == 1)
+        return *this;
+
+    VBezier bezier = *this;
+
+    VBezier result;
+    bezier.parameterSplitLeft(t0, &result);
+    float trueT = (t1-t0)/(1-t0);
+    bezier.parameterSplitLeft(trueT, &result);
+
+    return result;
+}
+
+float VBezier::tAtLength(float l) const
+{
+    float len = length();
+    float t   = 1.0;
+    const float error = 0.01;
+    if (l > len || floatCmp(l, len))
+        return t;
+
+    t *= 0.5;
+
+    float lastBigger = 1.0;
+    while (1) {
+        VBezier right = *this;
+        VBezier left;
+        right.parameterSplitLeft(t, &left);
+        float lLen = left.length();
+        if (fabs(lLen - l) < error)
+            break;
+
+        if (lLen < l) {
+            t += (lastBigger - t) * 0.5;
+        } else {
+            lastBigger = t;
+            t -= t * 0.5;
+        }
+    }
+    return t;
+}
+
+void
+VBezier::splitAtLength(float len, VBezier *left, VBezier *right)
+{
+   float t;
+
+   *right = *this;
+   t =  right->tAtLength(len);
+   right->parameterSplitLeft(t, left);
+}
+
+
+
diff --git a/src/vector/vbezier.h b/src/vector/vbezier.h
new file mode 100644 (file)
index 0000000..92f4418
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef VBEZIER_H
+#define VBEZIER_H
+
+#include <vpoint.h>
+
+class  VBezier
+{
+public:
+   VBezier(){}
+   VPointF pointAt(float t)const;
+   VBezier onInterval(float t0, float t1)const;
+   float length()const;
+   static void coefficients(float t, float &a, float &b, float &c, float &d);
+   static VBezier fromPoints(const VPointF &start, const VPointF &cp1, const VPointF &cp2, const VPointF &end);
+   inline void parameterSplitLeft(float t, VBezier *left);
+   inline void split(VBezier *firstHalf, VBezier *secondHalf) const;
+   float tAtLength(float len) const;
+   void splitAtLength(float len, VBezier *left, VBezier *right);
+   VPointF pt1() const { return VPointF(x1, y1); }
+   VPointF pt2() const { return VPointF(x2, y2); }
+   VPointF pt3() const { return VPointF(x3, y3); }
+   VPointF pt4() const { return VPointF(x4, y4); }
+private:
+   VPointF derivative(float t)const;
+   float x1,y1,x2,y2,x3,y3,x4,y4;
+};
+
+inline void VBezier::coefficients(float t, float &a, float &b, float &c, float &d)
+{
+    float m_t = 1. - t;
+    b = m_t * m_t;
+    c = t * t;
+    d = c * t;
+    a = b * m_t;
+    b *= 3. * t;
+    c *= 3. * m_t;
+}
+
+inline VPointF VBezier::pointAt(float t) const
+{
+    // numerically more stable:
+    float x, y;
+
+    float m_t = 1. - t;
+    {
+        float a = x1*m_t + x2*t;
+        float b = x2*m_t + x3*t;
+        float c = x3*m_t + x4*t;
+        a = a*m_t + b*t;
+        b = b*m_t + c*t;
+        x = a*m_t + b*t;
+    }
+    {
+        float a = y1*m_t + y2*t;
+        float b = y2*m_t + y3*t;
+        float c = y3*m_t + y4*t;
+        a = a*m_t + b*t;
+        b = b*m_t + c*t;
+        y = a*m_t + b*t;
+    }
+    return VPointF(x, y);
+}
+
+inline void VBezier::parameterSplitLeft(float t, VBezier *left)
+{
+    left->x1 = x1;
+    left->y1 = y1;
+
+    left->x2 = x1 + t * ( x2 - x1 );
+    left->y2 = y1 + t * ( y2 - y1 );
+
+    left->x3 = x2 + t * ( x3 - x2 ); // temporary holding spot
+    left->y3 = y2 + t * ( y3 - y2 ); // temporary holding spot
+
+    x3 = x3 + t * ( x4 - x3 );
+    y3 = y3 + t * ( y4 - y3 );
+
+    x2 = left->x3 + t * ( x3 - left->x3);
+    y2 = left->y3 + t * ( y3 - left->y3);
+
+    left->x3 = left->x2 + t * ( left->x3 - left->x2 );
+    left->y3 = left->y2 + t * ( left->y3 - left->y2 );
+
+    left->x4 = x1 = left->x3 + t * (x2 - left->x3);
+    left->y4 = y1 = left->y3 + t * (y2 - left->y3);
+}
+
+inline void VBezier::split(VBezier *firstHalf, VBezier *secondHalf) const
+{
+    float c = (x2 + x3)*.5;
+    firstHalf->x2 = (x1 + x2)*.5;
+    secondHalf->x3 = (x3 + x4)*.5;
+    firstHalf->x1 = x1;
+    secondHalf->x4 = x4;
+    firstHalf->x3 = (firstHalf->x2 + c)*.5;
+    secondHalf->x2 = (secondHalf->x3 + c)*.5;
+    firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
+
+    c = (y2 + y3)/2;
+    firstHalf->y2 = (y1 + y2)*.5;
+    secondHalf->y3 = (y3 + y4)*.5;
+    firstHalf->y1 = y1;
+    secondHalf->y4 = y4;
+    firstHalf->y3 = (firstHalf->y2 + c)*.5;
+    secondHalf->y2 = (secondHalf->y3 + c)*.5;
+    firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
+}
+
+
+#endif //VBEZIER_H
diff --git a/src/vector/vbitmap.cpp b/src/vector/vbitmap.cpp
new file mode 100644 (file)
index 0000000..4746df9
--- /dev/null
@@ -0,0 +1,282 @@
+#include "vbitmap.h"
+#include "vglobal.h"
+#include<string.h>
+
+struct VBitmapData
+{
+    ~VBitmapData();
+    VBitmapData();
+    static VBitmapData *create(int width, int height, VBitmap::Format format);
+    RefCount                  ref;
+    int                       width;
+    int                       height;
+    int                       depth;
+    int                       stride;
+    int                       nBytes;
+    VBitmap::Format           format;
+    uchar                     *data;
+    VBitmapCleanupFunction    cleanupFunction;
+    void*                     cleanupInfo;
+    uint                      ownData : 1;
+    uint                      roData : 1;
+};
+
+VBitmapData::~VBitmapData()
+{
+    if (cleanupFunction)
+        cleanupFunction(cleanupInfo);
+    if (data && ownData)
+        free(data);
+    data = 0;
+}
+
+VBitmapData::VBitmapData()
+    : ref(0), width(0), height(0), depth(0), stride(0),
+      format(VBitmap::Format::ARGB32), data(nullptr),
+      cleanupFunction(0), cleanupInfo(0), ownData(true), roData(false)
+{
+}
+
+VBitmapData * VBitmapData::create(int width, int height, VBitmap::Format format)
+{
+    if ((width <= 0) || (height <= 0) || format == VBitmap::Format::Invalid)
+        return nullptr;
+
+    int depth = 1;
+    switch (format) {
+    case VBitmap::Format::Alpha8:
+        depth = 8;
+    case VBitmap::Format::ARGB32:
+    case VBitmap::Format::ARGB32_Premultiplied:
+        depth = 32;
+        break;
+    default:
+        break;
+    }
+
+    const int stride = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 4)
+
+    VBitmapData *d = new VBitmapData;
+
+    d->width = width;
+    d->height = height;
+    d->depth = depth;
+    d->format = format;
+    d->stride = stride;
+    d->nBytes = d->stride*height;
+    d->data  = (uchar *)malloc(d->nBytes);
+
+    if (!d->data) {
+        delete d;
+        return 0;
+    }
+
+    return d;
+}
+
+inline void VBitmap::cleanUp(VBitmapData *d)
+{
+    delete d;
+}
+
+void VBitmap::detach()
+{
+    if (d) {
+        if (d->ref.isShared() || d->roData)
+            *this = copy();
+    }
+}
+
+VBitmap::~VBitmap()
+{
+    if (!d) return;
+
+    if (!d->ref.deref())
+        cleanUp(d);
+}
+
+VBitmap::VBitmap()
+        : d(nullptr)
+{
+
+}
+
+VBitmap::VBitmap(const VBitmap &other)
+{
+    d = other.d;
+    if (d)
+        d->ref.ref();
+}
+
+VBitmap::VBitmap(VBitmap &&other): d(other.d)
+{
+    other.d = nullptr;
+}
+
+VBitmap &VBitmap::operator=(const VBitmap &other)
+{
+    if (!d) {
+        d = other.d;
+        if (d)
+            d->ref.ref();
+    } else {
+        if (!d->ref.deref())
+            cleanUp(d);
+        other.d->ref.ref();
+        d = other.d;
+    }
+
+    return *this;
+}
+
+inline VBitmap &VBitmap::operator=(VBitmap &&other)
+{
+    if (d && !d->ref.deref())
+        cleanUp(d);
+    d = other.d;
+    return *this;
+}
+
+VBitmap::VBitmap(int w, int h, VBitmap::Format format)
+{
+
+}
+VBitmap::VBitmap(uchar *data, int w, int h, int bytesPerLine, VBitmap::Format format,
+                 VBitmapCleanupFunction f, void *cleanupInfo)
+{
+    d = new VBitmapData;
+    d->data = data;
+    d->format = format;
+    d->width = w;
+    d->height = h;
+    d->stride = bytesPerLine;
+    d->cleanupFunction = nullptr;
+    d->cleanupInfo = nullptr;
+    d->ownData = false;
+    d->roData = false;
+    d->ref = 1;
+}
+
+VBitmap VBitmap::copy(const VRect& r) const
+{
+    if (!d)
+        return VBitmap();
+
+    if (r.isNull()) {
+        VBitmap image(d->width, d->height, d->format);
+        if (image.isNull())
+            return image;
+
+        if (image.d->nBytes != d->nBytes) {
+            int bpl = vMin(stride(), image.stride());
+            for (int i = 0; i < height(); i++)
+                memcpy(image.scanLine(i), scanLine(i), bpl);
+        } else
+            memcpy(image.bits(), bits(), d->nBytes);
+        return image;
+    }
+
+    int x = r.x();
+    int y = r.y();
+    int w = r.width();
+    int h = r.height();
+
+    int dx = 0;
+    int dy = 0;
+    if (w <= 0 || h <= 0)
+        return VBitmap();
+
+    VBitmap image(w, h, d->format);
+    if (image.isNull())
+        return image;
+
+    if (x < 0 || y < 0 || x + w > d->width || y + h > d->height) {
+        // bitBlt will not cover entire image - clear it.
+        image.fill(0);
+        if (x < 0) {
+            dx = -x;
+            x = 0;
+        }
+        if (y < 0) {
+            dy = -y;
+            y = 0;
+        }
+    }
+    //TODO implement properly.
+}
+
+int VBitmap::stride() const
+{
+    return d ? d->stride : 0;
+}
+
+int VBitmap::width() const
+{
+    return d ? d->width : 0;
+}
+
+int VBitmap::height() const
+{
+    return d ? d->height : 0;
+}
+
+uchar *VBitmap::bits()
+{
+    if (!d)
+        return 0;
+    detach();
+
+    // In case detach ran out of memory...
+    if (!d)
+        return 0;
+
+    return d->data;
+}
+
+const uchar *VBitmap::bits() const
+{
+    return d ? d->data : 0;
+}
+
+bool VBitmap::isNull() const
+{
+    return !d;
+}
+
+uchar *VBitmap::scanLine(int i)
+{
+    if (!d)
+        return 0;
+
+    detach();
+
+    // In case detach() ran out of memory
+    if (!d)
+        return 0;
+
+    return d->data + i * d->stride;
+}
+
+const uchar *VBitmap::scanLine(int i) const
+{
+    if (!d)
+        return 0;
+
+    //assert(i >= 0 && i < height());
+    return d->data + i * d->stride;
+}
+
+VBitmap::Format VBitmap::format() const
+{
+    if (!d)
+        return VBitmap::Format::Invalid;
+    return d->format;
+}
+
+
+void VBitmap::fill(uint pixel)
+{
+    if (!d)
+        return;
+}
+
diff --git a/src/vector/vbitmap.h b/src/vector/vbitmap.h
new file mode 100644 (file)
index 0000000..5017063
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef VBITMAP_H
+#define VBITMAP_H
+
+#include "vrect.h"
+
+struct VBitmapData;
+typedef void (*VBitmapCleanupFunction)(void *);
+class VBitmap
+{
+public:
+    enum class Format {
+        Invalid,
+        Alpha8,
+        ARGB32,
+        ARGB32_Premultiplied,
+        Last
+    };
+    ~VBitmap();
+    VBitmap();
+    VBitmap(const VBitmap &other);
+    VBitmap(VBitmap &&other);
+    VBitmap &operator=(const VBitmap &);
+    VBitmap &operator=(VBitmap &&other);
+
+    VBitmap(int w, int h, VBitmap::Format format);
+    VBitmap(uchar *data, int w, int h, int bytesPerLine, VBitmap::Format format,
+            VBitmapCleanupFunction f = nullptr, void *cleanupInfo = nullptr);
+
+    VBitmap copy(const VRect &rect = VRect()) const;
+    void fill(uint pixel);
+
+    int width() const;
+    int height() const;
+    uchar *bits();
+    const uchar *bits() const;
+    uchar *scanLine(int);
+    const uchar *scanLine(int) const;
+    int stride() const;
+    bool isNull() const;
+    VBitmap::Format format() const;
+private:
+    void detach();
+    void cleanUp(VBitmapData *x);
+    VBitmapData    *d;
+};
+
+#endif // VBITMAP_H
diff --git a/src/vector/vbrush.cpp b/src/vector/vbrush.cpp
new file mode 100644 (file)
index 0000000..300afbc
--- /dev/null
@@ -0,0 +1,77 @@
+#include"vbrush.h"
+
+VGradient::VGradient(VGradient::Type type):mType(type), mSpread(VGradient::Spread::Pad), mMode(VGradient::Mode::Absolute)
+{
+
+}
+
+void VGradient::setStops(const VGradientStops &stops)
+{
+    mStops = stops;
+}
+
+VLinearGradient::VLinearGradient(const VPointF &start,
+                                 const VPointF &stop):VGradient(VGradient::Type::Linear)
+{
+    linear.x1 = start.x();
+    linear.y1 = start.y();
+    linear.x1 = stop.x();
+    linear.y1 = stop.y();
+}
+
+VLinearGradient::VLinearGradient(float xStart, float yStart,
+                                 float xStop, float yStop):VGradient(VGradient::Type::Linear)
+{
+    linear.x1 = xStart;
+    linear.y1 = yStart;
+    linear.x1 = xStop;
+    linear.y1 = yStop;
+}
+
+VRadialGradient::VRadialGradient(const VPointF &center, float cradius,
+                                 const VPointF &focalPoint, float fradius):VGradient(VGradient::Type::Radial)
+{
+    radial.cx = center.x();
+    radial.cy = center.y();
+    radial.fx = focalPoint.x();
+    radial.fy = focalPoint.y();
+    radial.cradius = cradius;
+    radial.fradius = fradius;
+}
+
+VRadialGradient::VRadialGradient(float cx, float cy, float cradius,
+                                 float fx, float fy, float fradius):VGradient(VGradient::Type::Radial)
+{
+    radial.cx = cx;
+    radial.cy = cy;
+    radial.fx = fx;
+    radial.fy = fy;
+    radial.cradius = cradius;
+    radial.fradius = fradius;
+}
+
+VBrush::VBrush(const VColor &color):mType(VBrush::Type::Solid),
+                                     mColor(color)
+{
+
+}
+
+VBrush::VBrush(int r, int g, int b, int a):mType(VBrush::Type::Solid),
+                                           mColor(r, g, b, a)
+
+{
+
+}
+
+VBrush::VBrush(const VGradient *gradient):mType(VBrush::Type::NoBrush)
+{
+    if (!gradient) return;
+
+    mGradient = gradient;
+
+    if (gradient->mType == VGradient::Type::Linear) {
+        mType = VBrush::Type::LinearGradient;
+    } else if (gradient->mType == VGradient::Type::Linear) {
+        mType = VBrush::Type::RadialGradient;
+    }
+}
diff --git a/src/vector/vbrush.h b/src/vector/vbrush.h
new file mode 100644 (file)
index 0000000..e93c9b1
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef VBRUSH_H
+#define VBRUSH_H
+
+#include"vglobal.h"
+#include"vpoint.h"
+#include"vmatrix.h"
+#include<vector>
+
+typedef std::pair<float, VColor>  VGradientStop;
+typedef std::vector<VGradientStop> VGradientStops;
+class VGradient
+{
+public:
+    enum class Mode {
+      Absolute,
+      Relative
+    };
+    enum class Spread {
+        Pad,
+        Repeat,
+        Reflect
+    };
+    enum class Type {
+        Linear,
+        Radial
+    };
+    VGradient(VGradient::Type type);
+    void setStops(const VGradientStops &stops);
+    VGradient(){}
+public:
+    static constexpr int   colorTableSize = 1024;
+    VGradient::Type        mType;
+    VGradient::Spread      mSpread;
+    VGradient::Mode        mMode;
+    VGradientStops         mStops;
+    union {
+        struct {
+            float x1, y1, x2, y2;
+        } linear;
+        struct {
+            float cx, cy, fx, fy, cradius, fradius;
+        } radial;
+    };
+    VMatrix mMatrix;
+};
+
+class VLinearGradient : public VGradient
+{
+public:
+    VLinearGradient(const VPointF &start, const VPointF &stop);
+    VLinearGradient(float xStart, float yStart, float xStop, float yStop);
+};
+
+class VRadialGradient : public VGradient
+{
+public:
+    VRadialGradient(const VPointF &center, float cradius, const VPointF &focalPoint, float fradius);
+    VRadialGradient(float cx, float cy, float cradius, float fx, float fy, float fradius);
+};
+
+class VBrush
+{
+public:
+    enum class Type {
+        NoBrush,
+        Solid,
+        LinearGradient,
+        RadialGradient,
+        Texture
+    };
+    VBrush():mType(Type::NoBrush){}
+    VBrush(const VColor &color);
+    VBrush(const VGradient *gradient);
+    VBrush(int r, int g, int b, int a);
+    inline VBrush::Type type() const{return mType;}
+public:
+    VBrush::Type       mType;
+    VColor             mColor;
+    const VGradient   *mGradient;
+};
+
+#endif // VBRUSH_H
diff --git a/src/vector/vcompositionfunctions.cpp b/src/vector/vcompositionfunctions.cpp
new file mode 100644 (file)
index 0000000..5122210
--- /dev/null
@@ -0,0 +1,99 @@
+#include"vdrawhelper.h"
+
+/*
+  result = s
+  dest = s * ca + d * cia
+*/
+void comp_func_solid_Source(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
+{
+    int ialpha, i;
+
+    if (const_alpha == 255)
+      {
+         memfill32(dest, color, length);
+      }
+    else
+      {
+         ialpha = 255 - const_alpha;
+         color = BYTE_MUL(color, const_alpha);
+         for (i = 0; i < length; ++i)
+           dest[i] = color + BYTE_MUL(dest[i], ialpha);
+      }
+}
+
+/*
+  r = s + d * sia
+  dest = r * ca + d * cia
+       =  (s + d * sia) * ca + d * cia
+       = s * ca + d * (sia * ca + cia)
+       = s * ca + d * (1 - sa*ca)
+       = s' + d ( 1 - s'a)
+*/
+void comp_func_solid_SourceOver(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
+{
+    int ialpha, i;
+
+    if (const_alpha != 255)
+      color = BYTE_MUL(color, const_alpha);
+    ialpha = 255 - vAlpha(color);
+    for (i = 0; i < length; ++i)
+      dest[i] = color + BYTE_MUL(dest[i], ialpha);
+}
+
+
+void comp_func_Source(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha)
+{
+    if (const_alpha == 255) {
+        memcpy(dest, src, size_t(length) * sizeof(uint));
+    } else {
+        uint ialpha = 255 - const_alpha;
+        for (int i = 0; i < length; ++i) {
+            dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha);
+        }
+    }
+}
+
+/* s' = s * ca
+ * d' = s' + d (1 - s'a)
+ */
+void comp_func_SourceOver(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha)
+{
+    uint s, sia;
+
+    if (const_alpha == 255) {
+        for (int i = 0; i < length; ++i) {
+            s = src[i];
+            if (s >= 0xff000000)
+                dest[i] = s;
+            else if (s != 0) {
+              sia = vAlpha(~s);
+              dest[i] = s + BYTE_MUL(dest[i], sia);
+            }
+        }
+    } else {
+        /* source' = source * const_alpha
+         * dest = source' + dest ( 1- source'a)
+         */
+        for (int i = 0; i < length; ++i) {
+            uint s = BYTE_MUL(src[i], const_alpha);
+            sia = vAlpha(~s);
+            dest[i] = s + BYTE_MUL(dest[i], sia);
+        }
+    }
+}
+
+
+CompositionFunctionSolid COMP_functionForModeSolid_C[] = {
+    comp_func_solid_Source,
+    comp_func_solid_SourceOver
+};
+
+CompositionFunction COMP_functionForMode_C[] = {
+    comp_func_Source,
+    comp_func_SourceOver
+};
+
+void vInitBlendFunctions()
+{
+}
+
diff --git a/src/vector/vdasher.cpp b/src/vector/vdasher.cpp
new file mode 100644 (file)
index 0000000..f819bf9
--- /dev/null
@@ -0,0 +1,240 @@
+#include"vdasher.h"
+#include"vbezier.h"
+
+class VLine
+{
+public:
+    VLine():mX1(0),mY1(0),mX2(0),mY2(0){}
+    VLine(float x1, float y1, float x2, float y2):mX1(x1),mY1(y1),mX2(x2),mY2(y2){}
+    VLine(const VPointF &p1, const VPointF &p2):mX1(p1.x()),mY1(p1.y()),mX2(p2.x()),mY2(p2.y()){}
+    float length() const;
+    void splitAtLength(float length, VLine &left, VLine &right) const;
+    VPointF p1() const {return VPointF(mX1, mY1);}
+    VPointF p2() const {return VPointF(mX2, mY2);}
+private:
+    float mX1;
+    float mY1;
+    float mX2;
+    float mY2;
+};
+
+// approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+// With alpha = 1, beta = 3/8, giving results with the largest error less
+// than 7% compared to the exact value.
+float
+VLine::length() const
+{
+   float x = mX2 - mX1;
+   float y = mY2 - mY1;
+   x = x < 0 ? -x : x;
+   y = y < 0 ? -y : y;
+   return (x > y ? x + 0.375 * y : y + 0.375 * x);
+}
+
+void
+VLine::splitAtLength(float lengthAt, VLine &left, VLine &right) const
+{
+   float len = length();
+   double dx = ((mX2 - mX1)/len) *lengthAt;
+   double dy = ((mY2 - mY1)/len) *lengthAt;
+
+   left.mX1 = mX1;
+   left.mY1 = mY1;
+   left.mX2 = left.mX1 + dx;
+   left.mY2 = left.mY1 + dy;
+
+   right.mX1 = left.mX2;
+   right.mY1 = left.mY2;
+   right.mX2 = mX2;
+   right.mY2 = mY2;
+}
+
+VDasher::VDasher(const float *dashArray, int size)
+{
+    if (!(size % 2))
+        vCritical<<"invalid dashArray format";
+
+    mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
+    mArraySize = size/2;
+    mDashOffset = dashArray[size-1];
+    mCurrentDashIndex = 0;
+    mCurrentDashLength = 0;
+    mIsCurrentOperationGap = false;
+}
+
+void VDasher::moveTo(const VPointF &p)
+{
+    mIsCurrentOperationGap = false;
+    mStartPt = p;
+    mCurPt = p;
+
+    if (!floatCmp(mDashOffset, 0.0)) {
+        float totalLength = 0.0;
+        for (int i = 0; i < mArraySize ; i++) {
+            totalLength = mDashArray[i].length + mDashArray[i].gap;
+        }
+        float normalizeLen = fmod(mDashOffset, totalLength);
+        if (normalizeLen < 0.0 ) {
+            normalizeLen = totalLength + normalizeLen;
+        }
+        // now the length is less than total length and +ve
+        // findout the current dash index , dashlength and gap.
+        for (int i = 0; i < mArraySize; i++) {
+            if (normalizeLen < mDashArray[i].length) {
+                mCurrentDashIndex = i;
+                mCurrentDashLength = mDashArray[i].length - normalizeLen;
+                mIsCurrentOperationGap = false;
+                break;
+            }
+            normalizeLen -= mDashArray[i].length;
+            if (normalizeLen < mDashArray[i].gap) {
+                mCurrentDashIndex = i;
+                mCurrentDashLength = mDashArray[i].gap - normalizeLen;
+                mIsCurrentOperationGap = true;
+                break;
+            }
+            normalizeLen -= mDashArray[i].gap;
+        }
+    } else {
+        mCurrentDashIndex = 0;
+        mCurrentDashLength = mDashArray[0].length;
+    }
+}
+
+void VDasher::lineTo(const VPointF &p)
+{
+    VLine left, right;
+    VLine line(mCurPt, p);
+    float length = line.length();
+    if (length < mCurrentDashLength) {
+         mCurrentDashLength -= length;
+         if (!mIsCurrentOperationGap) {
+             mDashedPath.moveTo(mCurPt);
+             mDashedPath.lineTo(p);
+         }
+    } else {
+         while (length > mCurrentDashLength) {
+              length -= mCurrentDashLength;
+              line.splitAtLength(mCurrentDashLength, left, right);
+              if (!mIsCurrentOperationGap) {
+                  mDashedPath.moveTo(left.p1());
+                  mDashedPath.lineTo(left.p2());
+                  mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
+              } else {
+                  mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
+                  mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
+              }
+              mIsCurrentOperationGap = !mIsCurrentOperationGap;
+              line = right;
+              mCurPt = line.p1();
+         }
+         // remainder
+         mCurrentDashLength -= length;
+         if (!mIsCurrentOperationGap) {
+             mDashedPath.moveTo(line.p1());
+             mDashedPath.lineTo(line.p2());
+         }
+         if (mCurrentDashLength < 1.0) {
+              // move to next dash
+              if (!mIsCurrentOperationGap) {
+                   mIsCurrentOperationGap = true;
+                   mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
+              } else {
+                   mIsCurrentOperationGap = false;
+                   mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
+                   mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
+              }
+         }
+    }
+    mCurPt = p;
+}
+
+void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
+{
+    VBezier left, right;
+    float bezLen = 0.0;
+    VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
+    bezLen = b.length();
+    if (bezLen < mCurrentDashLength) {
+         mCurrentDashLength -= bezLen;
+         if (!mIsCurrentOperationGap) {
+              mDashedPath.moveTo(mCurPt);
+              mDashedPath.cubicTo(cp1, cp2, e);
+         }
+    } else {
+         while (bezLen > mCurrentDashLength) {
+              bezLen -= mCurrentDashLength;
+              b.splitAtLength(mCurrentDashLength, &left, &right);
+              if (!mIsCurrentOperationGap) {
+                   mDashedPath.moveTo(left.pt1());
+                   mDashedPath.cubicTo(left.pt2(), left.pt3(), left.pt4());;
+                   mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
+              } else {
+                   mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
+                   mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
+              }
+              mIsCurrentOperationGap = !mIsCurrentOperationGap;
+              b = right;
+              mCurPt = b.pt1();
+        }
+          // remainder
+         mCurrentDashLength -= bezLen;
+         if (!mIsCurrentOperationGap) {
+              mDashedPath.moveTo(b.pt1());
+              mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
+         }
+         if (mCurrentDashLength < 1.0) {
+              // move to next dash
+              if (!mIsCurrentOperationGap)
+                {
+                   mIsCurrentOperationGap = true;
+                   mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
+                }
+              else
+                {
+                   mIsCurrentOperationGap = false;
+                   mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
+                   mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
+                }
+         }
+    }
+    mCurPt = e;
+}
+
+
+VPath VDasher::dashed(const VPath &path)
+{
+    if (path.isEmpty()) return VPath();
+
+    mDashedPath = VPath();
+    const std::vector<VPath::Element> &elms = path.elements();
+    const std::vector<VPointF> &pts = path.points();
+    const VPointF *ptPtr = pts.data();
+
+    for (auto i : elms) {
+        switch (i) {
+            case VPath::Element::MoveTo: {
+                moveTo(*ptPtr++);
+                break;
+            }
+            case VPath::Element::LineTo: {
+                lineTo(*ptPtr++);
+                break;
+            }
+            case VPath::Element::CubicTo: {
+                cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
+                ptPtr += 3;
+                break;
+            }
+            case VPath::Element::Close: {
+                // The point is already joined to start point in VPath
+                // no need to do anything here.
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return mDashedPath;
+}
+
diff --git a/src/vector/vdasher.h b/src/vector/vdasher.h
new file mode 100644 (file)
index 0000000..ed97558
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef VDASHER_H
+#define VDASHER_H
+#include "vpath.h"
+class VDasher
+{
+ public:
+    VDasher(const float *dashArray, int size);
+    VPath dashed(const VPath &path);
+ private:
+    void moveTo(const VPointF &p);
+    void lineTo(const VPointF &p);
+    void cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e);
+    void close();
+private:
+    struct Dash {
+      float length;
+      float gap;
+    };
+    const VDasher::Dash   *mDashArray;
+    int                    mArraySize;
+    VPointF               mStartPt;
+    VPointF               mCurPt;
+    int                    mCurrentDashIndex;
+    int                    mCurrentDashLength;
+    bool                   mIsCurrentOperationGap;
+    float                  mDashOffset;
+    VPath                 mDashedPath;
+};
+#endif // VDASHER_H
diff --git a/src/vector/vdebug.cpp b/src/vector/vdebug.cpp
new file mode 100644 (file)
index 0000000..de0fe79
--- /dev/null
@@ -0,0 +1,719 @@
+#include "vdebug.h"
+#include <cstring>
+#include <chrono>
+#include <ctime>
+#include <thread>
+#include <tuple>
+#include <atomic>
+#include <queue>
+#include <fstream>
+#include<sstream>
+
+namespace
+{
+
+    /* Returns microseconds since epoch */
+    uint64_t timestamp_now()
+    {
+        return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
+    }
+
+    /* I want [2016-10-13 00:01:23.528514] */
+    void format_timestamp(std::ostream & os, uint64_t timestamp)
+    {
+    // The next 3 lines do not work on MSVC!
+    // auto duration = std::chrono::microseconds(timestamp);
+    // std::chrono::high_resolution_clock::time_point time_point(duration);
+    // std::time_t time_t = std::chrono::high_resolution_clock::to_time_t(time_point);
+    std::time_t time_t = timestamp / 1000000;
+    auto gmtime = std::gmtime(&time_t);
+    char buffer[32];
+    strftime(buffer, 32, "%Y-%m-%d %T.", gmtime);
+    char microseconds[7];
+    sprintf(microseconds, "%06llu", timestamp % 1000000);
+    os << '[' << buffer << microseconds << ']';
+    }
+
+    std::thread::id this_thread_id()
+    {
+    static thread_local const std::thread::id id = std::this_thread::get_id();
+    return id;
+    }
+
+    template < typename T, typename Tuple >
+    struct TupleIndex;
+
+    template < typename T,typename ... Types >
+    struct TupleIndex < T, std::tuple < T, Types... > >
+    {
+    static constexpr const std::size_t value = 0;
+    };
+
+    template < typename T, typename U, typename ... Types >
+    struct TupleIndex < T, std::tuple < U, Types... > >
+    {
+    static constexpr const std::size_t value = 1 + TupleIndex < T, std::tuple < Types... > >::value;
+    };
+
+} // anonymous namespace
+
+    typedef std::tuple < char, uint32_t, uint64_t, int32_t, int64_t, double, VDebug::string_literal_t, char * > SupportedTypes;
+
+    char const * to_string(LogLevel loglevel)
+    {
+    switch (loglevel)
+    {
+    case LogLevel::OFF:
+        return "OFF";
+    case LogLevel::INFO:
+        return "INFO";
+    case LogLevel::WARN:
+        return "WARN";
+    case LogLevel::CRIT:
+        return "CRIT";
+    }
+    return "XXXX";
+    }
+
+    template < typename Arg >
+    void VDebug::encode(Arg arg)
+    {
+    *reinterpret_cast<Arg*>(buffer()) = arg;
+    m_bytes_used += sizeof(Arg);
+    }
+
+    template < typename Arg >
+    void VDebug::encode(Arg arg, uint8_t type_id)
+    {
+    resize_buffer_if_needed(sizeof(Arg) + sizeof(uint8_t));
+    encode < uint8_t >(type_id);
+    encode < Arg >(arg);
+    }
+
+    VDebug::VDebug(LogLevel level, char const * file, char const * function, uint32_t line)
+    : m_bytes_used(0)
+    , m_buffer_size(sizeof(m_stack_buffer))
+    {
+    encode < uint64_t >(timestamp_now());
+    encode < std::thread::id >(this_thread_id());
+    encode < string_literal_t >(string_literal_t(file));
+    encode < string_literal_t >(string_literal_t(function));
+    encode < uint32_t >(line);
+    encode < LogLevel >(level);
+    if (level == LogLevel::INFO) {
+        m_logAll = true;
+    }else {
+        m_logAll = true;
+    }
+    }
+
+    VDebug::~VDebug() = default;
+
+    void VDebug::stringify(std::ostream & os)
+    {
+    char * b = !m_heap_buffer ? m_stack_buffer : m_heap_buffer.get();
+    char const * const end = b + m_bytes_used;
+    uint64_t timestamp = *reinterpret_cast < uint64_t * >(b); b += sizeof(uint64_t);
+    std::thread::id threadid = *reinterpret_cast < std::thread::id * >(b); b += sizeof(std::thread::id);
+    string_literal_t file = *reinterpret_cast < string_literal_t * >(b); b += sizeof(string_literal_t);
+    string_literal_t function = *reinterpret_cast < string_literal_t * >(b); b += sizeof(string_literal_t);
+    uint32_t line = *reinterpret_cast < uint32_t * >(b); b += sizeof(uint32_t);
+    LogLevel loglevel = *reinterpret_cast < LogLevel * >(b); b += sizeof(LogLevel);
+    if (m_logAll) {
+    format_timestamp(os, timestamp);
+
+    os << '[' << to_string(loglevel) << ']'
+       << '[' << threadid << ']'
+       << '[' << file.m_s << ':' << function.m_s << ':' << line << "] ";
+
+    }
+
+    stringify(os, b, end);
+    os << std::endl;
+
+    if (loglevel >= LogLevel::CRIT)
+        os.flush();
+    }
+
+    template < typename Arg >
+    char * decode(std::ostream & os, char * b, Arg * dummy)
+    {
+    Arg arg = *reinterpret_cast < Arg * >(b);
+    os << arg;
+    return b + sizeof(Arg);
+    }
+
+    template <>
+    char * decode(std::ostream & os, char * b, VDebug::string_literal_t * dummy)
+    {
+    VDebug::string_literal_t s = *reinterpret_cast < VDebug::string_literal_t * >(b);
+    os << s.m_s;
+    return b + sizeof(VDebug::string_literal_t);
+    }
+
+    template <>
+    char * decode(std::ostream & os, char * b, char ** dummy)
+    {
+    while (*b != '\0')
+    {
+        os << *b;
+        ++b;
+    }
+    return ++b;
+    }
+
+    void VDebug::stringify(std::ostream & os, char * start, char const * const end)
+    {
+    if (start == end)
+        return;
+
+    int type_id = static_cast < int >(*start); start++;
+
+    switch (type_id)
+    {
+    case 0:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<0, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 1:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<1, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 2:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<2, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 3:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<3, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 4:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<4, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 5:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<5, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 6:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<6, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    case 7:
+        stringify(os, decode(os, start, static_cast<std::tuple_element<7, SupportedTypes>::type*>(nullptr)), end);
+        return;
+    }
+    }
+
+    char * VDebug::buffer()
+    {
+    return !m_heap_buffer ? &m_stack_buffer[m_bytes_used] : &(m_heap_buffer.get())[m_bytes_used];
+    }
+
+    void VDebug::resize_buffer_if_needed(size_t additional_bytes)
+    {
+    size_t const required_size = m_bytes_used + additional_bytes;
+
+    if (required_size <= m_buffer_size)
+        return;
+
+    if (!m_heap_buffer)
+    {
+        m_buffer_size = std::max(static_cast<size_t>(512), required_size);
+        m_heap_buffer.reset(new char[m_buffer_size]);
+        memcpy(m_heap_buffer.get(), m_stack_buffer, m_bytes_used);
+        return;
+    }
+    else
+    {
+        m_buffer_size = std::max(static_cast<size_t>(2 * m_buffer_size), required_size);
+        std::unique_ptr < char [] > new_heap_buffer(new char[m_buffer_size]);
+        memcpy(new_heap_buffer.get(), m_heap_buffer.get(), m_bytes_used);
+        m_heap_buffer.swap(new_heap_buffer);
+    }
+    }
+
+    void VDebug::encode(char const * arg)
+    {
+    if (arg != nullptr)
+        encode_c_string(arg, strlen(arg));
+    }
+
+    void VDebug::encode(char * arg)
+    {
+    if (arg != nullptr)
+        encode_c_string(arg, strlen(arg));
+    }
+
+    void VDebug::encode_c_string(char const * arg, size_t length)
+    {
+    if (length == 0)
+        return;
+
+    resize_buffer_if_needed(1 + length + 1);
+    char * b = buffer();
+    auto type_id = TupleIndex < char *, SupportedTypes >::value;
+    *reinterpret_cast<uint8_t*>(b++) = static_cast<uint8_t>(type_id);
+    memcpy(b, arg, length + 1);
+    m_bytes_used += 1 + length + 1;
+    }
+
+    void VDebug::encode(string_literal_t arg)
+    {
+    encode < string_literal_t >(arg, TupleIndex < string_literal_t, SupportedTypes >::value);
+    }
+
+    VDebug& VDebug::operator<<(std::string const & arg)
+    {
+    encode_c_string(arg.c_str(), arg.length());
+    return *this;
+    }
+
+    VDebug& VDebug::operator<<(int32_t arg)
+    {
+    encode < int32_t >(arg, TupleIndex < int32_t, SupportedTypes >::value);
+    return *this;
+    }
+
+    VDebug& VDebug::operator<<(uint32_t arg)
+    {
+    encode < uint32_t >(arg, TupleIndex < uint32_t, SupportedTypes >::value);
+    return *this;
+    }
+
+//    VDebug& VDebug::operator<<(int64_t arg)
+//    {
+//    encode < int64_t >(arg, TupleIndex < int64_t, SupportedTypes >::value);
+//    return *this;
+//    }
+
+//    VDebug& VDebug::operator<<(uint64_t arg)
+//    {
+//    encode < uint64_t >(arg, TupleIndex < uint64_t, SupportedTypes >::value);
+//    return *this;
+//    }
+    VDebug& VDebug::operator<<(unsigned long arg)
+    {
+        encode < uint64_t >(arg, TupleIndex < uint64_t, SupportedTypes >::value);
+        return *this;
+    }
+
+    VDebug& VDebug::operator<<(long arg)
+    {
+        encode < int64_t >(arg, TupleIndex < int64_t, SupportedTypes >::value);
+        return *this;
+    }
+
+    VDebug& VDebug::operator<<(double arg)
+    {
+    encode < double >(arg, TupleIndex < double, SupportedTypes >::value);
+    return *this;
+    }
+
+    VDebug& VDebug::operator<<(char arg)
+    {
+    encode < char >(arg, TupleIndex < char, SupportedTypes >::value);
+    return *this;
+    }
+
+    struct BufferBase
+    {
+    virtual ~BufferBase() = default;
+        virtual void push(VDebug && logline) = 0;
+    virtual bool try_pop(VDebug & logline) = 0;
+    };
+
+    struct SpinLock
+    {
+    SpinLock(std::atomic_flag & flag) : m_flag(flag)
+    {
+        while (m_flag.test_and_set(std::memory_order_acquire));
+    }
+
+    ~SpinLock()
+    {
+        m_flag.clear(std::memory_order_release);
+    }
+
+    private:
+    std::atomic_flag & m_flag;
+    };
+
+    /* Multi Producer Single Consumer Ring Buffer */
+    class RingBuffer : public BufferBase
+    {
+    public:
+        struct alignas(64) Item
+        {
+        Item()
+        : flag()
+        , written(0)
+        , logline(LogLevel::INFO, nullptr, nullptr, 0)
+        {
+        }
+
+        std::atomic_flag flag;
+        char written;
+        char padding[256 - sizeof(std::atomic_flag) - sizeof(char) - sizeof(VDebug)];
+        VDebug logline;
+        };
+
+        RingBuffer(size_t const size)
+            : m_size(size)
+            , m_ring(static_cast<Item*>(std::malloc(size * sizeof(Item))))
+            , m_write_index(0)
+            , m_read_index(0)
+        {
+            for (size_t i = 0; i < m_size; ++i)
+            {
+            new (&m_ring[i]) Item();
+            }
+        static_assert(sizeof(Item) == 256, "Unexpected size != 256");
+        }
+
+        ~RingBuffer()
+        {
+            for (size_t i = 0; i < m_size; ++i)
+            {
+            m_ring[i].~Item();
+            }
+            std::free(m_ring);
+        }
+
+        void push(VDebug && logline) override
+        {
+            unsigned int write_index = m_write_index.fetch_add(1, std::memory_order_relaxed) % m_size;
+            Item & item = m_ring[write_index];
+            SpinLock spinlock(item.flag);
+        item.logline = std::move(logline);
+        item.written = 1;
+        }
+
+        bool try_pop(VDebug & logline) override
+        {
+            Item & item = m_ring[m_read_index % m_size];
+            SpinLock spinlock(item.flag);
+            if (item.written == 1)
+            {
+            logline = std::move(item.logline);
+            item.written = 0;
+        ++m_read_index;
+            return true;
+            }
+            return false;
+        }
+
+        RingBuffer(RingBuffer const &) = delete;
+        RingBuffer& operator=(RingBuffer const &) = delete;
+
+    private:
+        size_t const m_size;
+        Item * m_ring;
+        std::atomic < unsigned int > m_write_index;
+    char pad[64];
+        unsigned int m_read_index;
+    };
+
+
+    class Buffer
+    {
+    public:
+        struct Item
+        {
+        Item(VDebug && logline) : logline(std::move(logline)) {}
+        char padding[256 - sizeof(VDebug)];
+        VDebug logline;
+        };
+
+    static constexpr const size_t size = 32768; // 8MB. Helps reduce memory fragmentation
+
+        Buffer() : m_buffer(static_cast<Item*>(std::malloc(size * sizeof(Item))))
+        {
+            for (size_t i = 0; i <= size; ++i)
+            {
+            m_write_state[i].store(0, std::memory_order_relaxed);
+            }
+        static_assert(sizeof(Item) == 256, "Unexpected size != 256");
+        }
+
+        ~Buffer()
+        {
+        unsigned int write_count = m_write_state[size].load();
+            for (size_t i = 0; i < write_count; ++i)
+            {
+            m_buffer[i].~Item();
+            }
+            std::free(m_buffer);
+        }
+
+    // Returns true if we need to switch to next buffer
+        bool push(VDebug && logline, unsigned int const write_index)
+        {
+        new (&m_buffer[write_index]) Item(std::move(logline));
+        m_write_state[write_index].store(1, std::memory_order_release);
+        return m_write_state[size].fetch_add(1, std::memory_order_acquire) + 1 == size;
+        }
+
+        bool try_pop(VDebug & logline, unsigned int const read_index)
+        {
+        if (m_write_state[read_index].load(std::memory_order_acquire))
+        {
+        Item & item = m_buffer[read_index];
+        logline = std::move(item.logline);
+        return true;
+        }
+        return false;
+        }
+
+        Buffer(Buffer const &) = delete;
+        Buffer& operator=(Buffer const &) = delete;
+
+    private:
+        Item * m_buffer;
+    std::atomic < unsigned int > m_write_state[size + 1];
+    };
+
+    class QueueBuffer : public BufferBase
+    {
+    public:
+    QueueBuffer(QueueBuffer const &) = delete;
+    QueueBuffer& operator=(QueueBuffer const &) = delete;
+
+    QueueBuffer() : m_current_read_buffer{nullptr}
+                , m_write_index(0)
+              , m_flag()
+              , m_read_index(0)
+    {
+        setup_next_write_buffer();
+    }
+
+        void push(VDebug && logline) override
+        {
+            unsigned int write_index = m_write_index.fetch_add(1, std::memory_order_relaxed);
+        if (write_index < Buffer::size)
+        {
+        if (m_current_write_buffer.load(std::memory_order_acquire)->push(std::move(logline), write_index))
+        {
+            setup_next_write_buffer();
+        }
+        }
+        else
+        {
+        while (m_write_index.load(std::memory_order_acquire) >= Buffer::size);
+        push(std::move(logline));
+        }
+        }
+
+        bool try_pop(VDebug & logline) override
+    {
+        if (m_current_read_buffer == nullptr)
+        m_current_read_buffer = get_next_read_buffer();
+
+        Buffer * read_buffer = m_current_read_buffer;
+
+        if (read_buffer == nullptr)
+        return false;
+
+        if (bool success = read_buffer->try_pop(logline, m_read_index))
+        {
+        m_read_index++;
+        if (m_read_index == Buffer::size)
+        {
+            m_read_index = 0;
+            m_current_read_buffer = nullptr;
+            SpinLock spinlock(m_flag);
+            m_buffers.pop();
+        }
+        return true;
+        }
+
+        return false;
+    }
+
+    private:
+    void setup_next_write_buffer()
+    {
+        std::unique_ptr < Buffer > next_write_buffer(new Buffer());
+        m_current_write_buffer.store(next_write_buffer.get(), std::memory_order_release);
+        SpinLock spinlock(m_flag);
+        m_buffers.push(std::move(next_write_buffer));
+        m_write_index.store(0, std::memory_order_relaxed);
+    }
+
+    Buffer * get_next_read_buffer()
+    {
+        SpinLock spinlock(m_flag);
+        return m_buffers.empty() ? nullptr : m_buffers.front().get();
+    }
+
+    private:
+    std::queue < std::unique_ptr < Buffer > > m_buffers;
+        std::atomic < Buffer * > m_current_write_buffer;
+    Buffer * m_current_read_buffer;
+        std::atomic < unsigned int > m_write_index;
+    std::atomic_flag m_flag;
+        unsigned int m_read_index;
+    };
+
+    class FileWriter
+    {
+    public:
+    FileWriter(std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb)
+        : m_log_file_roll_size_bytes(log_file_roll_size_mb * 1024 * 1024)
+        , m_name(log_directory + log_file_name)
+    {
+        roll_file();
+    }
+
+    void write(VDebug & logline)
+    {
+        auto pos = m_os->tellp();
+        logline.stringify(*m_os);
+        m_bytes_written += m_os->tellp() - pos;
+        if (m_bytes_written > m_log_file_roll_size_bytes)
+        {
+        roll_file();
+        }
+    }
+
+    private:
+    void roll_file()
+    {
+        if (m_os)
+        {
+        m_os->flush();
+        m_os->close();
+        }
+
+        m_bytes_written = 0;
+        m_os.reset(new std::ofstream());
+        // TODO Optimize this part. Does it even matter ?
+        std::string log_file_name = m_name;
+        log_file_name.append(".");
+        log_file_name.append(std::to_string(++m_file_number));
+        log_file_name.append(".txt");
+        m_os->open(log_file_name, std::ofstream::out | std::ofstream::trunc);
+    }
+
+    private:
+    uint32_t m_file_number = 0;
+    std::streamoff m_bytes_written = 0;
+    uint32_t const m_log_file_roll_size_bytes;
+    std::string const m_name;
+    std::unique_ptr < std::ofstream > m_os;
+    };
+
+    class NanoLogger
+    {
+    public:
+    NanoLogger(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb)
+        : m_state(State::INIT)
+        , m_buffer_base(new RingBuffer(std::max(1u, ngl.ring_buffer_size_mb) * 1024 * 4))
+        , m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb))
+        , m_thread(&NanoLogger::pop, this)
+    {
+        m_state.store(State::READY, std::memory_order_release);
+    }
+
+    NanoLogger(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb)
+        : m_state(State::INIT)
+        , m_buffer_base(new QueueBuffer())
+        , m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb))
+        , m_thread(&NanoLogger::pop, this)
+    {
+        m_state.store(State::READY, std::memory_order_release);
+    }
+
+    ~NanoLogger()
+    {
+        m_state.store(State::SHUTDOWN);
+        m_thread.join();
+    }
+
+    void add(VDebug && logline)
+    {
+        m_buffer_base->push(std::move(logline));
+    }
+
+    void pop()
+    {
+        // Wait for constructor to complete and pull all stores done there to this thread / core.
+        while (m_state.load(std::memory_order_acquire) == State::INIT)
+        std::this_thread::sleep_for(std::chrono::microseconds(50));
+
+        VDebug logline(LogLevel::INFO, nullptr, nullptr, 0);
+
+        while (m_state.load() == State::READY)
+        {
+        if (m_buffer_base->try_pop(logline))
+            m_file_writer.write(logline);
+        else
+            std::this_thread::sleep_for(std::chrono::microseconds(50));
+        }
+
+        // Pop and log all remaining entries
+        while (m_buffer_base->try_pop(logline))
+        {
+        m_file_writer.write(logline);
+        }
+    }
+
+    private:
+    enum class State
+    {
+        INIT,
+        READY,
+        SHUTDOWN
+    };
+
+    std::atomic < State > m_state;
+    std::unique_ptr < BufferBase > m_buffer_base;
+    FileWriter m_file_writer;
+    std::thread m_thread;
+    };
+
+    std::unique_ptr < NanoLogger > nanologger;
+    std::atomic < NanoLogger * > atomic_nanologger;
+
+    bool VDebugServer::operator==(VDebug & logline)
+    {
+    atomic_nanologger.load(std::memory_order_acquire)->add(std::move(logline));
+    return true;
+    }
+
+    void initialize(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb)
+    {
+    nanologger.reset(new NanoLogger(ngl, log_directory, log_file_name, log_file_roll_size_mb));
+    atomic_nanologger.store(nanologger.get(), std::memory_order_seq_cst);
+    }
+
+    void initialize(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb)
+    {
+    nanologger.reset(new NanoLogger(gl, log_directory, log_file_name, log_file_roll_size_mb));
+    atomic_nanologger.store(nanologger.get(), std::memory_order_seq_cst);
+    }
+
+    std::atomic < unsigned int > loglevel = {0};
+
+    void set_log_level(LogLevel level)
+    {
+    loglevel.store(static_cast<unsigned int>(level), std::memory_order_release);
+    }
+
+    bool is_logged(LogLevel level)
+    {
+    return static_cast<unsigned int>(level) >= loglevel.load(std::memory_order_relaxed);
+    }
+
+    void initDebug()
+    {
+        initialize(GuaranteedLogger(), "/tmp/", "lotti-player", 1);
+        set_log_level(LogLevel::INFO);
+    }
+
+#ifndef DEBUG_CONSTRUCTOR_FUNCTION
+# define DEBUG_CONSTRUCTOR_FUNCTION0(AFUNC) \
+    namespace { \
+    static const struct AFUNC ## _ctor_class_ { \
+        inline AFUNC ## _ctor_class_() { AFUNC(); } \
+    } AFUNC ## _ctor_instance_; \
+    }
+
+# define DEBUG_CONSTRUCTOR_FUNCTION(AFUNC) DEBUG_CONSTRUCTOR_FUNCTION0(AFUNC)
+#endif
+
+   DEBUG_CONSTRUCTOR_FUNCTION(initDebug)
+
diff --git a/src/vector/vdebug.h b/src/vector/vdebug.h
new file mode 100644 (file)
index 0000000..536bc48
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef VDEBUG_H
+#define VDEBUG_H
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <iosfwd>
+#include <type_traits>
+
+
+
+enum class LogLevel : uint8_t { INFO, WARN, CRIT, OFF };
+
+
+class VDebug
+{
+public:
+    VDebug();
+    VDebug& debug() {return *this;}
+    VDebug(LogLevel level, char const * file, char const * function, uint32_t line);
+    ~VDebug();
+
+    VDebug(VDebug &&) = default;
+    VDebug& operator=(VDebug &&) = default;
+
+    void stringify(std::ostream & os);
+
+    VDebug& operator<<(char arg);
+    VDebug& operator<<(int32_t arg);
+    VDebug& operator<<(uint32_t arg);
+    //VDebug& operator<<(int64_t arg);
+    //VDebug& operator<<(uint64_t arg);
+
+    VDebug& operator<<(long arg);
+    VDebug& operator<<(unsigned long arg);
+    VDebug& operator<<(double arg);
+    VDebug& operator<<(std::string const & arg);
+
+    template < size_t N >
+    VDebug& operator<<(const char (&arg)[N]) {
+        encode(string_literal_t(arg));
+        return *this;
+    }
+
+    template < typename Arg >
+    typename std::enable_if < std::is_same < Arg, char const * >::value, VDebug& >::type
+    operator<<(Arg const & arg) {
+        encode(arg);
+        return *this;
+    }
+
+    template < typename Arg >
+    typename std::enable_if < std::is_same < Arg, char * >::value, VDebug& >::type
+    operator<<(Arg const & arg) {
+        encode(arg);
+        return *this;
+    }
+
+    struct string_literal_t {
+        explicit string_literal_t(char const * s) : m_s(s) {}
+        char const * m_s;
+    };
+
+private:
+    char * buffer();
+
+    template < typename Arg >
+    void encode(Arg arg);
+
+    template < typename Arg >
+    void encode(Arg arg, uint8_t type_id);
+
+    void encode(char * arg);
+    void encode(char const * arg);
+    void encode(string_literal_t arg);
+    void encode_c_string(char const * arg, size_t length);
+    void resize_buffer_if_needed(size_t additional_bytes);
+    void stringify(std::ostream & os, char * start, char const * const end);
+
+private:
+    size_t m_bytes_used;
+    size_t m_buffer_size;
+    std::unique_ptr < char [] > m_heap_buffer;
+    bool m_logAll;
+    char m_stack_buffer[256 - sizeof(bool) - 2 * sizeof(size_t) - sizeof(decltype(m_heap_buffer)) - 8 /* Reserved */];
+};
+
+struct VDebugServer
+{
+    /*
+     * Ideally this should have been operator+=
+     * Could not get that to compile, so here we are...
+     */
+    bool operator==(VDebug &);
+};
+
+void set_log_level(LogLevel level);
+
+bool is_logged(LogLevel level);
+
+
+/*
+ * Non guaranteed logging. Uses a ring buffer to hold log lines.
+ * When the ring gets full, the previous log line in the slot will be dropped.
+ * Does not block producer even if the ring buffer is full.
+ * ring_buffer_size_mb - LogLines are pushed into a mpsc ring buffer whose size
+ * is determined by this parameter. Since each LogLine is 256 bytes,
+ * ring_buffer_size = ring_buffer_size_mb * 1024 * 1024 / 256
+ */
+struct NonGuaranteedLogger
+{
+    NonGuaranteedLogger(uint32_t ring_buffer_size_mb_) : ring_buffer_size_mb(ring_buffer_size_mb_) {}
+    uint32_t ring_buffer_size_mb;
+};
+
+/*
+ * Provides a guarantee log lines will not be dropped.
+ */
+struct GuaranteedLogger
+{
+};
+
+/*
+ * Ensure initialize() is called prior to any log statements.
+ * log_directory - where to create the logs. For example - "/tmp/"
+ * log_file_name - root of the file name. For example - "nanolog"
+ * This will create log files of the form -
+ * /tmp/nanolog.1.txt
+ * /tmp/nanolog.2.txt
+ * etc.
+ * log_file_roll_size_mb - mega bytes after which we roll to next log file.
+ */
+void initialize(GuaranteedLogger gl, std::string const & log_directory,
+                std::string const & log_file_name, uint32_t log_file_roll_size_mb);
+void initialize(NonGuaranteedLogger ngl, std::string const & log_directory,
+                std::string const & log_file_name, uint32_t log_file_roll_size_mb);
+
+
+#define VDEBUG_LOG(LEVEL) VDebugServer() == VDebug(LEVEL, __FILE__, __func__, __LINE__).debug()
+#define vDebug is_logged(LogLevel::INFO) && VDEBUG_LOG(LogLevel::INFO)
+#define vWarning is_logged(LogLevel::WARN) && VDEBUG_LOG(LogLevel::WARN)
+#define vCritical is_logged(LogLevel::CRIT) && VDEBUG_LOG(LogLevel::CRIT)
+
+#endif // VDEBUG_H
diff --git a/src/vector/vdrawhelper.cpp b/src/vector/vdrawhelper.cpp
new file mode 100644 (file)
index 0000000..641d3d6
--- /dev/null
@@ -0,0 +1,544 @@
+#include"vdrawhelper.h"
+#include<cstring>
+#include<climits>
+#include<unordered_map>
+#include<mutex>
+
+
+class VGradientCache
+{
+public:
+    struct CacheInfo : public VSpanData::Pinnable
+    {
+        inline CacheInfo(VGradientStops s):stops(s) {}
+        uint32_t buffer32[VGradient::colorTableSize];
+        VGradientStops stops;
+        bool           alpha;
+    };
+
+    typedef std::unordered_multimap<uint64_t, std::shared_ptr<const CacheInfo>> VGradientColorTableHash;
+    bool generateGradientColorTable(const VGradientStops &stops,
+                                    uint32_t *colorTable, int size);
+    inline const std::shared_ptr<const CacheInfo> getBuffer(const VGradient &gradient)
+    {
+        uint64_t hash_val = 0;
+        std::shared_ptr<const CacheInfo> info;
+
+        const VGradientStops &stops = gradient.mStops;
+        for (uint i = 0; i < stops.size() && i <= 2; i++)
+            hash_val += stops[i].second.premulARGB();
+
+        cacheAccess.lock();
+
+        int count = cache.count(hash_val);
+        if (!count) {
+            // key is not present in the hash
+            info =  addCacheElement(hash_val, gradient);
+        } else if (count == 1) {
+            VGradientColorTableHash::const_iterator it = cache.find(hash_val);
+            if (it->second->stops == stops) {
+                info = it->second;
+            } else {
+                // didn't find an exact match
+                info =  addCacheElement(hash_val, gradient);
+            }
+        } else {
+            // we have a multiple data with same key
+            auto range = cache.equal_range(hash_val);
+            for (auto i = range.first; i != range.second; ++i) {
+                if (i->second->stops == stops) {
+                    info = i->second;
+                    break;
+                }
+            }
+            if (!info) {
+                // didn't find an exact match
+                info =  addCacheElement(hash_val, gradient);
+            }
+        }
+        cacheAccess.unlock();
+        return info;
+    }
+
+protected:
+    inline uint maxCacheSize() const { return 60; }
+    const std::shared_ptr<const CacheInfo> addCacheElement(uint64_t hash_val, const VGradient &gradient)
+    {
+        if (cache.size() == maxCacheSize()) {
+            int count = rand() % maxCacheSize();
+            while (count--) {
+                cache.erase(cache.begin());
+            }
+        }
+        auto cache_entry = std::make_shared<CacheInfo>(gradient.mStops);
+        cache_entry->alpha = generateGradientColorTable(gradient.mStops, cache_entry->buffer32, VGradient::colorTableSize);
+        cache.insert(std::make_pair(hash_val, cache_entry));
+        return cache_entry;
+    }
+
+    VGradientColorTableHash cache;
+    std::mutex cacheAccess;
+};
+
+bool
+VGradientCache::generateGradientColorTable(const VGradientStops &stops,
+                                           uint32_t *colorTable, int size)
+{
+    int dist, idist, pos = 0, i;
+    bool  alpha = false;
+    int stopCount = stops.size();
+    const VGradientStop *curr, *next, *start;
+    uint32_t curColor, nextColor;
+    float delta, t, incr, fpos;
+
+    start = stops.data();
+    curr = start;
+    if (!curr->second.isOpaque()) alpha = true;
+    curColor = curr->second.premulARGB();
+    incr = 1.0 / (float)size;
+    fpos = 1.5 * incr;
+
+    colorTable[pos++] = curColor;
+
+    while (fpos <= curr->first) {
+        colorTable[pos] = colorTable[pos - 1];
+        pos++;
+        fpos += incr;
+    }
+
+   for (i = 0; i < stopCount - 1; ++i) {
+        curr = (start + i);
+        next = (start + i + 1);
+        delta = 1/(next->first - curr->first);
+        if (!next->second.isOpaque()) alpha = true;
+        nextColor = next->second.premulARGB();
+        while (fpos < next->first && pos < size)
+          {
+             t = (fpos - curr->first) * delta;
+             dist = (int)(255 * t);
+             idist = 255 - dist;
+             colorTable[pos] = INTERPOLATE_PIXEL_255(curColor, idist, nextColor, dist);
+             ++pos;
+             fpos += incr;
+          }
+        curColor = nextColor;
+     }
+
+   for (;pos < size; ++pos)
+     colorTable[pos] = curColor;
+
+   // Make sure the last color stop is represented at the end of the table
+   colorTable[size-1] = curColor;
+   return alpha;
+}
+
+static VGradientCache  VGradientCacheInstance;
+
+void VRasterBuffer::init()
+{
+   mBuffer = nullptr;
+   mWidth = 0;
+   mHeight = 0;
+   mCompositionMode = VPainter::CompModeSrcOver;
+}
+
+void VRasterBuffer::clear()
+{
+    memset(mBuffer, 0, mHeight * mBytesPerLine);
+}
+
+VBitmap::Format VRasterBuffer::prepare(VBitmap *image)
+{
+    mBuffer = (uchar *)image->bits();
+    mWidth = image->width();
+    mHeight = image->height();
+    mBytesPerPixel = 4;
+    mBytesPerLine = image->stride();
+
+    mFormat = image->format();
+    //drawHelper = qDrawHelper + format;
+    return mFormat;
+}
+
+void VSpanData::init(VRasterBuffer* image)
+{
+    mRasterBuffer = image;
+    mSystemClip = VRect(0,0, image->width(), image->height());
+    mType = VSpanData::Type::None;
+    mBlendFunc = nullptr;
+    mUnclippedBlendFunc = nullptr;
+}
+
+extern CompositionFunction      COMP_functionForMode_C[];
+extern CompositionFunctionSolid COMP_functionForModeSolid_C[];
+static const CompositionFunction *functionForMode = COMP_functionForMode_C;
+static const CompositionFunctionSolid *functionForModeSolid = COMP_functionForModeSolid_C;
+
+/*
+ *  Gradient Draw routines
+ *
+ */
+
+#define FIXPT_BITS 8
+#define FIXPT_SIZE (1<<FIXPT_BITS)
+static inline void
+getLinearGradientValues(LinearGradientValues *v, const VSpanData *data)
+{
+    const VGradientData *grad = &data->mGradient;
+    v->dx = grad->linear.x2 - grad->linear.x1;
+    v->dy = grad->linear.y2 - grad->linear.y1;
+    v->l = v->dx * v->dx + v->dy * v->dy;
+    v->off = 0;
+    if (v->l != 0) {
+        v->dx /= v->l;
+        v->dy /= v->l;
+        v->off = -v->dx * grad->linear.x1 - v->dy * grad->linear.y1;
+    }
+}
+
+static inline int
+gradientClamp(const VGradientData *grad, int ipos)
+{
+   int limit;
+
+   if (grad->mSpread == VGradient::Spread::Repeat)
+     {
+        ipos = ipos % VGradient::colorTableSize;
+        ipos = ipos < 0 ? VGradient::colorTableSize + ipos : ipos;
+     }
+   else if (grad->mSpread == VGradient::Spread::Reflect)
+     {
+        limit = VGradient::colorTableSize * 2;
+        ipos = ipos % limit;
+        ipos = ipos < 0 ? limit + ipos : ipos;
+        ipos = ipos >= VGradient::colorTableSize ? limit - 1 - ipos : ipos;
+     }
+   else
+     {
+        if (ipos < 0) ipos = 0;
+        else if (ipos >= VGradient::colorTableSize)
+          ipos = VGradient::colorTableSize - 1;
+     }
+   return ipos;
+}
+
+static uint32_t
+gradientPixelFixed(const VGradientData *grad, int fixed_pos)
+{
+   int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
+
+   return grad->mColorTable[gradientClamp(grad, ipos)];
+}
+
+static inline uint32_t
+gradientPixel(const VGradientData *grad, float pos)
+{
+   int ipos = (int)(pos * (VGradient::colorTableSize - 1) + (float)(0.5));
+
+   return grad->mColorTable[gradientClamp(grad, ipos)];
+}
+
+void
+fetch_linear_gradient(uint32_t *buffer, const Operator *op, const VSpanData *data, int y, int x, int length)
+{
+    float t, inc;
+    const VGradientData *gradient = &data->mGradient;
+
+    bool affine = true;
+    float rx=0, ry=0;
+    if (op->linear.l == 0) {
+        t = inc = 0;
+    } else {
+        rx = data->m21 * (y + float(0.5)) + data->m11 * (x + float(0.5)) + data->dx;
+        ry = data->m22 * (y + float(0.5)) + data->m12 * (x + float(0.5)) + data->dy;
+        t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off;
+        inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
+        affine = !data->m13 && !data->m23;
+
+        if (affine) {
+            t *= (VGradient::colorTableSize - 1);
+            inc *= (VGradient::colorTableSize - 1);
+        }
+    }
+
+    const uint32_t *end = buffer + length;
+    if (affine) {
+        if (inc > float(-1e-5) && inc < float(1e-5)) {
+            memfill32(buffer, gradientPixelFixed(gradient, int(t * FIXPT_SIZE)), length);
+        } else {
+            if (t+inc*length < float(INT_MAX >> (FIXPT_BITS + 1)) &&
+                t+inc*length > float(INT_MIN >> (FIXPT_BITS + 1))) {
+                // we can use fixed point math
+                int t_fixed = int(t * FIXPT_SIZE);
+                int inc_fixed = int(inc * FIXPT_SIZE);
+                while (buffer < end) {
+                    *buffer = gradientPixelFixed(gradient, t_fixed);
+                    t_fixed += inc_fixed;
+                    ++buffer;
+                }
+            } else {
+                // we have to fall back to float math
+                while (buffer < end) {
+                    *buffer = gradientPixel(gradient, t/VGradient::colorTableSize);
+                    t += inc;
+                    ++buffer;
+                }
+            }
+        }
+    } else { // fall back to float math here as well
+        float rw = data->m23 * (y + float(0.5)) + data->m13 * (x + float(0.5)) + data->m33;
+        while (buffer < end) {
+            float x = rx/rw;
+            float y = ry/rw;
+            t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off;
+
+            *buffer = gradientPixel(gradient, t);
+            rx += data->m11;
+            ry += data->m12;
+            rw += data->m13;
+            if (!rw) {
+                rw += data->m13;
+            }
+            ++buffer;
+        }
+    }
+}
+
+
+static inline Operator getOperator(const VSpanData *data, const VRle::Span *spans, int spanCount)
+{
+    Operator op;
+    bool solidSource = false;
+
+    switch(data->mType) {
+    case VSpanData::Type::Solid:
+        solidSource = vAlpha(data->mSolid) & 0xFF;
+        op.srcFetch = nullptr;
+        break;
+    case VSpanData::Type::LinearGradient:
+        solidSource = false;
+        getLinearGradientValues(&op.linear, data);
+        op.srcFetch = &fetch_linear_gradient;
+        break;
+    default:
+        break;
+    }
+
+    op.mode = data->mRasterBuffer->mCompositionMode;
+    if (op.mode == VPainter::CompModeSrcOver && solidSource)
+        op.mode = VPainter::CompModeSrc;
+
+    op.funcSolid = functionForModeSolid[op.mode];
+    op.func = functionForMode[op.mode];
+
+    return op;
+}
+
+static void
+blendColorARGB(int count, const VRle::Span *spans, void *userData)
+{
+    VSpanData *data = (VSpanData *)(userData);
+    Operator op = getOperator(data, spans, count);
+    const uint color = data->mSolid;
+
+    if (op.mode == VPainter::CompModeSrc) {
+        // inline for performance
+        while (count--) {
+            uint *target = ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
+            if (spans->coverage == 255) {
+                memfill32(target, color, spans->len);
+            } else {
+                uint c = BYTE_MUL(color, spans->coverage);
+                int ialpha = 255 - spans->coverage;
+                for (int i = 0; i < spans->len; ++i)
+                    target[i] = c + BYTE_MUL(target[i], ialpha);
+            }
+            ++spans;
+        }
+        return;
+    }
+
+    while (count--) {
+        uint *target = ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
+        op.funcSolid(target, spans->len, color, spans->coverage);
+        ++spans;
+    }
+}
+
+#define BLEND_GRADIENT_BUFFER_SIZE 2048
+static void
+blendGradientARGB(int count, const VRle::Span *spans, void *userData)
+{
+    VSpanData *data = (VSpanData *)(userData);
+    Operator op = getOperator(data, spans, count);
+
+    unsigned int buffer[BLEND_GRADIENT_BUFFER_SIZE];
+
+    if (!op.srcFetch)
+      return;
+
+    while (count--) {
+        uint *target = ((uint *)data->mRasterBuffer->scanLine(spans->y)) + spans->x;
+        int length = spans->len;
+        while (length) {
+            int l = std::min(length, BLEND_GRADIENT_BUFFER_SIZE);
+            op.srcFetch(buffer, &op, data, spans->y, spans->x, l);
+            op.func(target, buffer, l, spans->coverage);
+            target += l;
+            length -= l;
+        }
+        ++spans;
+    }
+}
+
+void
+VSpanData::setup(const VBrush &brush, VPainter::CompositionMode mode, int alpha)
+{
+    switch (brush.type()) {
+    case VBrush::Type::NoBrush:
+        mType = VSpanData::Type::None;
+        break;
+    case VBrush::Type::Solid:
+        mType = VSpanData::Type::Solid;
+        mSolid = brush.mColor.premulARGB();
+        break;
+    case VBrush::Type::LinearGradient: {
+        mType = VSpanData::Type::LinearGradient;
+        auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
+        mGradient.mColorTable = cacheInfo->buffer32;
+        mGradient.mColorTableAlpha = cacheInfo->alpha;
+        mGradient.linear.x1 = brush.mGradient->linear.x1;
+        mGradient.linear.y1 = brush.mGradient->linear.y1;
+        mGradient.linear.x2 = brush.mGradient->linear.x2;
+        mGradient.linear.y2 = brush.mGradient->linear.y2;
+        mGradient.mSpread = brush.mGradient->mSpread;
+        setupMatrix(brush.mGradient->mMatrix);
+        break;
+    }
+    case VBrush::Type::RadialGradient: {
+        mType = VSpanData::Type::RadialGradient;
+        auto cacheInfo = VGradientCacheInstance.getBuffer(*brush.mGradient);
+        mGradient.mColorTable = cacheInfo->buffer32;
+        mGradient.mColorTableAlpha = cacheInfo->alpha;
+        mGradient.radial.cx = brush.mGradient->radial.cx;
+        mGradient.radial.cy = brush.mGradient->radial.cy;
+        mGradient.radial.fx = brush.mGradient->radial.fx;
+        mGradient.radial.fy = brush.mGradient->radial.fy;
+        mGradient.radial.cradius = brush.mGradient->radial.cradius;
+        mGradient.radial.fradius = brush.mGradient->radial.fradius;
+        mGradient.mSpread = brush.mGradient->mSpread;
+        setupMatrix(brush.mGradient->mMatrix);
+        break;
+    }
+    default:
+        break;
+    }
+    updateSpanFunc();
+}
+
+void VSpanData::setupMatrix(const VMatrix &matrix)
+{
+    VMatrix inv = matrix.inverted();
+    m11 = inv.m11();
+    m12 = inv.m12();
+    m13 = inv.m13();
+    m21 = inv.m21();
+    m22 = inv.m22();
+    m23 = inv.m23();
+    m33 = inv.m33();
+    dx = inv.m31();
+    dy = inv.m32();
+
+    //const bool affine = inv.isAffine();
+//    fast_matrix = affine
+//        && m11 * m11 + m21 * m21 < 1e4
+//        && m12 * m12 + m22 * m22 < 1e4
+//        && fabs(dx) < 1e4
+//        && fabs(dy) < 1e4;
+}
+
+void
+VSpanData::updateSpanFunc()
+{
+    switch (mType) {
+    case VSpanData::Type::None:
+        mUnclippedBlendFunc = nullptr;
+        break;
+    case VSpanData::Type::Solid:
+        mUnclippedBlendFunc = &blendColorARGB;
+        break;
+    case VSpanData::Type::LinearGradient:
+    case VSpanData::Type::RadialGradient: {
+        mUnclippedBlendFunc = &blendGradientARGB;
+        break;
+    }
+    default:
+        break;
+    }
+}
+
+#if !defined(__SSE2__) && !defined(__ARM_NEON__)
+void
+memfill32(uint32_t *dest, uint32_t value, int length)
+{
+   int n;
+
+   if (length <= 0)
+     return;
+
+   // Cute hack to align future memcopy operation
+   // and do unroll the loop a bit. Not sure it is
+   // the most efficient, but will do for now.
+   n = (length + 7) / 8;
+   switch (length & 0x07)
+     {
+        case 0: do { *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 7:      *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 6:      *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 5:      *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 4:      *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 3:      *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 2:      *dest++ = value;
+           VECTOR_FALLTHROUGH;
+        case 1:      *dest++ = value;
+        } while (--n > 0);
+     }
+}
+#endif
+
+void vInitDrawhelperFunctions()
+{
+    vInitBlendFunctions();
+
+#if defined(__ARM_NEON__)
+    // update fast path for NEON
+    extern void comp_func_solid_SourceOver_neon(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
+    extern void comp_func_solid_Source_neon(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
+
+    COMP_functionForModeSolid_C[VPainter::CompModeSrc] = comp_func_solid_Source_neon;
+    COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] = comp_func_solid_SourceOver_neon;
+#endif
+
+#if defined(__SSE2__)
+    // update fast path for SSE2
+    extern void comp_func_solid_SourceOver_sse2(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
+    extern void comp_func_solid_Source_sse2(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
+    extern void comp_func_Source_sse2(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha);
+    extern void comp_func_SourceOver_sse2(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha);
+
+    COMP_functionForModeSolid_C[VPainter::CompModeSrc] = comp_func_solid_Source_sse2;
+    COMP_functionForModeSolid_C[VPainter::CompModeSrcOver] = comp_func_solid_SourceOver_sse2;
+
+    COMP_functionForMode_C[VPainter::CompModeSrc] = comp_func_Source_sse2;
+    COMP_functionForMode_C[VPainter::CompModeSrcOver] = comp_func_SourceOver_sse2;
+#endif
+}
+
+V_CONSTRUCTOR_FUNCTION(vInitDrawhelperFunctions)
+
diff --git a/src/vector/vdrawhelper.h b/src/vector/vdrawhelper.h
new file mode 100644 (file)
index 0000000..0826f00
--- /dev/null
@@ -0,0 +1,182 @@
+#ifndef VDRAWHELPER_H
+#define VDRAWHELPER_H
+
+#include"vrect.h"
+#include"vbrush.h"
+#include"vrle.h"
+#include"vpainter.h"
+#include"vbitmap.h"
+#include"assert.h"
+#include<cstring>
+
+struct VSpanData;
+struct Operator;
+
+typedef void (*CompositionFunctionSolid)(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha);
+typedef void (*CompositionFunction)(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha);
+typedef void (*SourceFetchProc)(uint32_t *buffer, const Operator *o, const VSpanData *data, int y, int x, int length);
+typedef void (*ProcessRleSpan)(int count, const VRle::Span *spans, void *userData);
+
+extern void memfill32(uint32_t *dest, uint32_t value, int count);
+
+
+struct LinearGradientValues
+{
+    float dx;
+    float dy;
+    float l;
+    float off;
+};
+
+struct RadialGradientValues
+{
+    float dx;
+    float dy;
+    float dr;
+    float sqrfr;
+    float a;
+    float inv2a;
+    bool extended;
+};
+
+struct Operator
+{
+    VPainter::CompositionMode mode;
+    SourceFetchProc srcFetch;
+    CompositionFunctionSolid funcSolid;
+    CompositionFunction func;
+    union {
+        LinearGradientValues linear;
+        RadialGradientValues radial;
+    };
+};
+
+class VRasterBuffer
+{
+public:
+    VRasterBuffer(){ init();}
+    void init();
+    VBitmap::Format prepare(VBitmap *image);
+    void prepare(int w, int h);
+    void prepareBuffer(int w, int h);
+    void clear();
+
+    void resetBuffer(int val=0);
+
+    inline uchar *scanLine(int y) { assert(y>=0); assert(y<mHeight); return mBuffer + y * mBytesPerLine; }
+
+    int width() const { return mWidth; }
+    int height() const { return mHeight; }
+    int bytesPerLine() const { return mBytesPerLine; }
+    int bytesPerPixel() const { return mBytesPerPixel; }
+
+    uchar *buffer() const { return mBuffer; }
+
+    VBitmap::Format mFormat;
+    VPainter::CompositionMode mCompositionMode;
+private:
+    int mWidth;
+    int mHeight;
+    int mBytesPerLine;
+    int mBytesPerPixel;
+    uchar *mBuffer;
+};
+
+struct VGradientData
+{
+    VGradient::Spread mSpread;
+    union {
+        struct {
+            float x1, y1, x2, y2;
+        } linear;
+        struct {
+            float cx, cy, fx, fy, cradius, fradius;
+        } radial;
+    };
+    const uint32_t  *mColorTable;
+    bool             mColorTableAlpha;
+};
+
+struct VSpanData
+{
+    class Pinnable {
+    protected:
+        ~Pinnable() {}
+    };
+    enum class Type {
+        None,
+        Solid,
+        LinearGradient,
+        RadialGradient
+    };
+
+    void updateSpanFunc();
+    void init(VRasterBuffer* image);
+    void setup(const VBrush &brush, VPainter::CompositionMode mode=VPainter::CompModeSrcOver, int alpha=255);
+    void setupMatrix(const VMatrix &matrix);
+
+    VRasterBuffer    *mRasterBuffer;
+    ProcessRleSpan    mBlendFunc;
+    ProcessRleSpan    mUnclippedBlendFunc;
+    VRect            mSystemClip;
+    VSpanData::Type   mType;
+    std::shared_ptr<VSpanData::Pinnable> mCachedGradient;
+    union {
+        uint32_t       mSolid;
+        VGradientData  mGradient;
+    };
+    float            m11, m12, m13, m21, m22, m23, m33, dx, dy;   // inverse xform matrix
+};
+
+void vInitDrawhelperFunctions();
+extern void vInitBlendFunctions();
+
+#define BYTE_MUL(c, a) \
+ ( (((((c) >> 8) & 0x00ff00ff) * (a)) & 0xff00ff00) + \
+   (((((c) & 0x00ff00ff) * (a)) >> 8) & 0x00ff00ff) )
+
+inline constexpr int vRed(uint32_t c)
+{ return ((c >> 16) & 0xff); }
+
+inline constexpr int vGreen(uint32_t c)
+{ return ((c >> 8) & 0xff); }
+
+inline constexpr int vBlue(uint32_t c)
+{ return (c & 0xff); }
+
+inline constexpr int vAlpha(uint32_t c)
+{ return c >> 24; }
+
+static inline uint
+INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b)
+{
+   uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
+   t >>= 8;
+   t &= 0xff00ff;
+   x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
+   x &= 0xff00ff00;
+   x |= t;
+   return x;
+}
+
+
+
+#define LOOP_ALIGNED_U1_A4(DEST, LENGTH, UOP, A4OP) \
+   { \
+      while((uintptr_t)DEST & 0xF && LENGTH) UOP \
+   \
+      while(LENGTH) { \
+         switch(LENGTH) { \
+            case 3: \
+            case 2: \
+            case 1: \
+               UOP \
+               break; \
+            default: \
+               A4OP \
+               break; \
+         } \
+      } \
+   }
+
+#endif //QDRAWHELPER_P_H
diff --git a/src/vector/vdrawhelper_neon.cpp b/src/vector/vdrawhelper_neon.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/vector/vdrawhelper_sse2.cpp b/src/vector/vdrawhelper_sse2.cpp
new file mode 100644 (file)
index 0000000..18b4acf
--- /dev/null
@@ -0,0 +1,299 @@
+#if defined(__SSE2__)
+
+#include"vdrawhelper.h"
+
+#include <xmmintrin.h> /* for _mm_shuffle_pi16 and _MM_SHUFFLE */
+#include <emmintrin.h> /* for SSE2 intrinsics */
+
+// Each 32bits components of alphaChannel must be in the form 0x00AA00AA
+inline static __m128i
+v4_byte_mul_sse2(__m128i c, __m128i a)
+{
+   const __m128i ag_mask = _mm_set1_epi32(0xFF00FF00);
+   const __m128i rb_mask = _mm_set1_epi32(0x00FF00FF);
+
+   /* for AG */
+   __m128i v_ag = _mm_and_si128(ag_mask, c);
+   v_ag = _mm_srli_epi32(v_ag, 8);
+   v_ag = _mm_mullo_epi16(a, v_ag);
+   v_ag = _mm_and_si128(ag_mask, v_ag);
+
+   /* for RB */
+   __m128i v_rb = _mm_and_si128(rb_mask, c);
+   v_rb = _mm_mullo_epi16(a, v_rb);
+   v_rb = _mm_srli_epi32(v_rb, 8);
+   v_rb = _mm_and_si128(rb_mask, v_rb);
+
+   /* combine */
+   return _mm_add_epi32(v_ag, v_rb);
+}
+
+static inline __m128i
+v4_ialpha_sse2(__m128i c)
+{
+   __m128i a = _mm_srli_epi32(c, 24);
+
+   return _mm_sub_epi32(_mm_set1_epi32(0xff), a);
+}
+
+static inline __m128i
+v4_interpolate_color_sse2(__m128i a, __m128i c0, __m128i c1)
+{
+   const __m128i rb_mask = _mm_set1_epi32(0xFF00FF00);
+   const __m128i zero = _mm_setzero_si128();
+
+   __m128i a_l = a;
+   __m128i a_h = a;
+   a_l = _mm_unpacklo_epi16(a_l, a_l);
+   a_h = _mm_unpackhi_epi16(a_h, a_h);
+
+   __m128i a_t = _mm_slli_epi64(a_l, 32);
+   __m128i a_t0 = _mm_slli_epi64(a_h, 32);
+
+   a_l = _mm_add_epi32(a_l, a_t);
+   a_h = _mm_add_epi32(a_h, a_t0);
+
+   __m128i c0_l = c0;
+   __m128i c0_h = c0;
+
+   c0_l = _mm_unpacklo_epi8(c0_l, zero);
+   c0_h = _mm_unpackhi_epi8(c0_h, zero);
+
+   __m128i c1_l = c1;
+   __m128i c1_h = c1;
+
+   c1_l = _mm_unpacklo_epi8(c1_l, zero);
+   c1_h = _mm_unpackhi_epi8(c1_h, zero);
+
+   __m128i cl_sub = _mm_sub_epi16(c0_l, c1_l);
+   __m128i ch_sub = _mm_sub_epi16(c0_h, c1_h);
+
+   cl_sub = _mm_mullo_epi16(cl_sub, a_l);
+   ch_sub = _mm_mullo_epi16(ch_sub, a_h);
+
+   __m128i c1ls = _mm_slli_epi16(c1_l, 8);
+   __m128i c1hs = _mm_slli_epi16(c1_h, 8);
+
+   cl_sub = _mm_add_epi16(cl_sub, c1ls);
+   ch_sub = _mm_add_epi16(ch_sub, c1hs);
+
+   cl_sub = _mm_and_si128(cl_sub, rb_mask);
+   ch_sub = _mm_and_si128(ch_sub, rb_mask);
+
+   cl_sub = _mm_srli_epi64(cl_sub, 8);
+   ch_sub = _mm_srli_epi64(ch_sub, 8);
+
+   cl_sub = _mm_packus_epi16(cl_sub, cl_sub);
+   ch_sub = _mm_packus_epi16(ch_sub, ch_sub);
+
+   return  (__m128i) _mm_shuffle_ps( (__m128)cl_sub, (__m128)ch_sub, 0x44);
+}
+
+
+
+// Load src and dest vector
+#define V4_FETCH_SRC_DEST \
+  __m128i v_src = _mm_loadu_si128((__m128i *)src); \
+  __m128i v_dest = _mm_load_si128((__m128i *)dest);
+
+#define V4_FETCH_SRC \
+  __m128i v_src = _mm_loadu_si128((__m128i *)src);
+
+#define V4_STORE_DEST \
+  _mm_store_si128((__m128i *)dest, v_src);
+
+#define V4_SRC_DEST_LEN_INC \
+  dest += 4; src +=4; length -= 4;
+
+// Multiply src color with const_alpha
+#define V4_ALPHA_MULTIPLY \
+  v_src = v4_byte_mul_sse2(v_src, v_alpha);
+
+// dest = src + dest * sia
+#define V4_COMP_OP_SRC_OVER \
+  __m128i v_sia = v4_ialpha_sse2(v_src); \
+  v_sia = _mm_add_epi32(v_sia, _mm_slli_epi32(v_sia, 16)); \
+  v_dest = v4_byte_mul_sse2(v_dest, v_sia); \
+  v_src = _mm_add_epi32(v_src, v_dest);
+
+// dest = src + dest * sia
+#define V4_COMP_OP_SRC \
+  v_src = v4_interpolate_color_sse2(v_alpha, v_src, v_dest);
+
+
+void
+memfill32(uint32_t *dest, uint32_t value, int length)
+{
+    __m128i vector_data = _mm_set_epi32(value, value, value, value);
+
+    // run till memory alligned to 16byte memory
+    while (length && ((uintptr_t)dest & 0xf))
+    {
+        *dest++ = value;
+        length--;
+    }
+
+    while (length >= 32)
+    {
+        _mm_store_si128 ((__m128i*)(dest),     vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 4),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 8),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 12),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 16),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 20),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 24),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 28), vector_data);
+
+        dest += 32;
+        length -= 32;
+    }
+
+    if (length >= 16)
+    {
+        _mm_store_si128 ((__m128i*)(dest),     vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 4),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 8),  vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 12),  vector_data);
+
+        dest += 16;
+        length -= 16;
+    }
+
+    if (length >= 8)
+    {
+        _mm_store_si128 ((__m128i*)(dest),     vector_data);
+        _mm_store_si128 ((__m128i*)(dest + 4),  vector_data);
+
+        dest += 8;
+        length -= 8;
+    }
+
+    if (length >= 4)
+    {
+        _mm_store_si128 ((__m128i*)(dest),     vector_data);
+
+        dest += 4;
+        length -= 4;
+    }
+
+    while (length)
+    {
+        *dest++ = value;
+        length--;
+    }
+}
+
+// dest = color + (dest * alpha)
+inline static void
+comp_func_helper_sse2(uint32_t *dest, int length, uint32_t color, uint32_t alpha)
+{
+   const __m128i v_color = _mm_set1_epi32(color);
+   const __m128i v_a = _mm_set1_epi16(alpha);
+
+   LOOP_ALIGNED_U1_A4(dest, length,
+      { /* UOP */
+         *dest = color + BYTE_MUL(*dest, alpha);
+         dest++; length--;
+      },
+      { /* A4OP */
+         __m128i v_dest = _mm_load_si128((__m128i *)dest);
+
+         v_dest = v4_byte_mul_sse2(v_dest, v_a);
+         v_dest = _mm_add_epi32(v_dest, v_color);
+
+         _mm_store_si128((__m128i *)dest, v_dest);
+
+         dest += 4; length -= 4;
+      })
+}
+
+void
+comp_func_solid_Source_sse2(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
+{
+   if (const_alpha == 255)
+     {
+        memfill32(dest, color, length);
+     }
+   else
+     {
+        int ialpha;
+
+        ialpha = 255 - const_alpha;
+        color = BYTE_MUL(color, const_alpha);
+        comp_func_helper_sse2(dest, length, color, ialpha);
+     }
+}
+
+void
+comp_func_solid_SourceOver_sse2(uint32_t *dest, int length, uint32_t color, uint32_t const_alpha)
+{
+   int ialpha;
+
+   if (const_alpha != 255)
+     color = BYTE_MUL(color, const_alpha);
+   ialpha = 255 - vAlpha(color);
+   comp_func_helper_sse2(dest, length, color, ialpha);
+}
+
+void comp_func_Source_sse2(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha)
+{
+    int ialpha;
+    if (const_alpha == 255) {
+         memcpy(dest, src, length * sizeof(uint32_t));
+    } else {
+        ialpha = 255 - const_alpha;
+        __m128i v_alpha = _mm_set1_epi32(const_alpha);
+
+        LOOP_ALIGNED_U1_A4(dest, length,
+          { /* UOP */
+             *dest = INTERPOLATE_PIXEL_255(*src, const_alpha, *dest, ialpha);
+             dest++; src++; length--;
+          },
+          { /* A4OP */
+             V4_FETCH_SRC_DEST
+             V4_COMP_OP_SRC
+             V4_STORE_DEST
+             V4_SRC_DEST_LEN_INC
+          })
+    }
+}
+
+void comp_func_SourceOver_sse2(uint32_t *dest, const uint32_t *src, int length, uint32_t const_alpha)
+{
+    uint32_t s, sia;
+
+    if (const_alpha == 255) {
+        LOOP_ALIGNED_U1_A4(dest, length,
+         { /* UOP */
+            s = *src;
+            sia = vAlpha(~s);
+            *dest = s + BYTE_MUL(*dest, sia);
+            dest++; src++; length--;
+         },
+         { /* A4OP */
+            V4_FETCH_SRC_DEST
+            V4_COMP_OP_SRC_OVER
+            V4_STORE_DEST
+            V4_SRC_DEST_LEN_INC
+         })
+    } else {
+        __m128i v_alpha = _mm_set1_epi32(const_alpha);
+        LOOP_ALIGNED_U1_A4(dest, length,
+         { /* UOP */
+           s = BYTE_MUL(*src, const_alpha);
+           sia = vAlpha(~s);
+           *dest = s + BYTE_MUL(*dest, sia);
+           dest++; src++; length--;
+         },
+         { /* A4OP */
+           V4_FETCH_SRC_DEST
+           V4_ALPHA_MULTIPLY
+           V4_COMP_OP_SRC_OVER
+           V4_STORE_DEST
+           V4_SRC_DEST_LEN_INC
+         })
+    }
+}
+
+
+#endif
diff --git a/src/vector/velapsedtimer.cpp b/src/vector/velapsedtimer.cpp
new file mode 100644 (file)
index 0000000..e4d516c
--- /dev/null
@@ -0,0 +1,29 @@
+#include"velapsedtimer.h"
+
+void VElapsedTimer::start()
+{
+    clock = std::chrono::high_resolution_clock::now();
+    m_valid = true;
+}
+
+double VElapsedTimer::restart()
+{
+    double elapsedTime = elapsed();
+    start();
+    return elapsedTime;
+}
+
+double VElapsedTimer::elapsed() const
+{
+    if (!isValid()) return 0;
+    return std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now()-clock).count();
+}
+
+bool VElapsedTimer::hasExpired(double time)
+{
+    double elapsedTime = elapsed();
+    if (elapsedTime > time)
+        return true;
+    return false;
+}
+
diff --git a/src/vector/velapsedtimer.h b/src/vector/velapsedtimer.h
new file mode 100644 (file)
index 0000000..070c7d8
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef VELAPSEDTIMER_H
+#define VELAPSEDTIMER_H
+
+#include "vglobal.h"
+#include<chrono>
+
+class  VElapsedTimer
+{
+public:
+    VElapsedTimer():m_valid(false){}
+    double elapsed() const;
+    bool hasExpired(double millsec);
+    void start();
+    double restart();
+    inline bool isValid() const {return m_valid;}
+private:
+    std::chrono::high_resolution_clock::time_point clock;
+    bool m_valid;
+};
+#endif // VELAPSEDTIMER_H
diff --git a/src/vector/vglobal.h b/src/vector/vglobal.h
new file mode 100644 (file)
index 0000000..b531344
--- /dev/null
@@ -0,0 +1,220 @@
+#ifndef VGLOBAL_H
+#define VGLOBAL_H
+
+#include<iostream>
+#include <cmath>
+#include<cstdint>
+#include<type_traits>
+#include<utility>
+
+typedef uint32_t         uint;
+typedef uint16_t         ushort;
+typedef uint8_t          uchar;
+
+
+#define V_UNUSED __attribute__((__unused__))
+#define V_REQUIRED_RESULT __attribute__ ((__warn_unused_result__))
+
+#define V_CONSTEXPR constexpr
+#define V_NOTHROW   noexcept
+
+#include"vdebug.h"
+
+#define VECTOR_FALLTHROUGH
+
+class RefCount
+{
+public:
+    inline RefCount(){}
+    inline RefCount(int i):atomic(i){}
+    inline bool ref() {
+        int count = atomic;
+        if (count == 0) // !isSharable
+            return false;
+        if (count != -1) // !isStatic
+            atomic++;
+        return true;
+    }
+    inline bool deref() {
+        int count = atomic;
+        if (count == 0) // !isSharable
+            return false;
+        if (count == -1) // isStatic
+            return true;
+        return --atomic;
+    }
+    bool isShared() const
+    {
+        int count = atomic;
+        return (count != 1) && (count != 0);
+    }
+    bool isStatic() const
+    {
+        // Persistent object, never deleted
+        return atomic == -1;
+    }
+    inline int count()const{return atomic;}
+    void setOwned() { atomic = 1; }
+    void setUnsharable() { atomic = 0; }
+private:
+    int atomic;
+};
+
+template <typename T>
+V_CONSTEXPR inline const T &vMin(const T &a, const T &b) { return (a < b) ? a : b; }
+template <typename T>
+V_CONSTEXPR inline const T &vMax(const T &a, const T &b) { return (a < b) ? b : a; }
+
+static inline bool vCompare(double p1, double p2)
+{
+    return (std::abs(p1 - p2) * 1000000000000. <= vMin(std::abs(p1), std::abs(p2)));
+}
+
+static inline bool vCompare(float p1, float p2)
+{
+    return (std::abs(p1 - p2) * 100000.f <= vMin(std::abs(p1), std::abs(p2)));
+}
+
+static inline bool floatCmp(float p1, float p2)
+{
+    return (std::abs(p1 - p2) * 100000.f <= fminf(std::abs(p1), std::abs(p2)));
+}
+
+static inline bool floatNull(float f)
+{
+    return std::abs(f) <= 0.00001f;
+}
+
+
+static inline bool vIsNull(double d)
+{
+    return std::abs(d) <= 0.000000000001;
+}
+
+static inline bool vIsNull(float f)
+{
+    return std::abs(f) <= 0.00001f;
+}
+
+class vFlagHelper
+{
+    int i;
+public:
+    constexpr inline vFlagHelper(int ai) noexcept : i(ai) {}
+    constexpr inline operator int() const noexcept { return i; }
+
+    constexpr inline vFlagHelper(uint ai) noexcept : i(int(ai)) {}
+    constexpr inline vFlagHelper(short ai) noexcept : i(int(ai)) {}
+    constexpr inline vFlagHelper(ushort ai) noexcept : i(int(uint(ai))) {}
+    constexpr inline operator uint() const noexcept { return uint(i); }
+};
+
+template<typename Enum>
+class vFlag
+{
+public:
+    static_assert((sizeof(Enum) <= sizeof(int)),
+                  "vFlag only supports int as storage so bigger type will overflow");
+    static_assert((std::is_enum<Enum>::value), "vFlag is only usable on enumeration types.");
+
+    typedef typename std::conditional<
+            std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
+            unsigned int,
+            signed int
+        >::type Int;
+
+    typedef Enum enum_type;
+    // compiler-generated copy/move ctor/assignment operators are fine!
+
+    constexpr inline vFlag(Enum f) noexcept : i(Int(f)) {}
+    constexpr inline vFlag() noexcept : i(0) {}
+    constexpr inline vFlag(vFlagHelper f) noexcept : i(f) {}
+
+     inline vFlag &operator&=(int mask) noexcept { i &= mask; return *this; }
+     inline vFlag &operator&=(uint mask) noexcept { i &= mask; return *this; }
+     inline vFlag &operator&=(Enum mask) noexcept { i &= Int(mask); return *this; }
+     inline vFlag &operator|=(vFlag f) noexcept { i |= f.i; return *this; }
+     inline vFlag &operator|=(Enum f) noexcept { i |= Int(f); return *this; }
+     inline vFlag &operator^=(vFlag f) noexcept { i ^= f.i; return *this; }
+     inline vFlag &operator^=(Enum f) noexcept { i ^= Int(f); return *this; }
+
+    constexpr inline operator Int() const noexcept { return i; }
+
+    constexpr inline vFlag operator|(vFlag f) const  { return vFlag(vFlagHelper(i | f.i)); }
+    constexpr inline vFlag operator|(Enum f) const noexcept { return vFlag(vFlagHelper(i | Int(f))); }
+    constexpr inline vFlag operator^(vFlag f) const noexcept { return vFlag(vFlagHelper(i ^ f.i)); }
+    constexpr inline vFlag operator^(Enum f) const noexcept { return vFlag(vFlagHelper(i ^ Int(f))); }
+    constexpr inline vFlag operator&(int mask) const noexcept { return vFlag(vFlagHelper(i & mask)); }
+    constexpr inline vFlag operator&(uint mask) const noexcept { return vFlag(vFlagHelper(i & mask)); }
+    constexpr inline vFlag operator&(Enum f) const noexcept { return vFlag(vFlagHelper(i & Int(f))); }
+    constexpr inline vFlag operator~() const noexcept { return vFlag(vFlagHelper(~i)); }
+
+    constexpr inline bool operator!() const noexcept { return !i; }
+
+    constexpr inline bool testFlag(Enum f) const noexcept { return (i & Int(f)) == Int(f) && (Int(f) != 0 || i == Int(f) ); }
+    inline vFlag &setFlag(Enum f, bool on = true) noexcept
+    {
+        return on ? (*this |= f) : (*this &= ~f);
+    }
+
+    Int i;
+};
+
+
+class  VColor
+{
+public:
+    inline VColor() noexcept {a = r = g = b = 0;}
+    inline VColor(int red, int green, int blue, int alpha = 255) noexcept { r = red; g = green; b = blue; a = alpha; }
+    inline int red() const noexcept {return r;}
+    inline int green() const noexcept{return g;}
+    inline int blue() const noexcept{return b;}
+    inline int alpha() const noexcept{return a;}
+    inline void setRed(int red) noexcept {r = red;}
+    inline void setGreen(int green)noexcept {g = green;}
+    inline void setBlue(int blue)noexcept {b = blue;}
+    inline void setAlpha(int alpha)noexcept {a = alpha;}
+    inline bool isOpaque() const{return a == 255;}
+    inline bool operator==(const VColor &o) const { return ((a==o.a) && (r==o.r) && (g == o.g) && (b == o.b));}
+    uint premulARGB() const {
+        int pr= (r * a)/255;
+        int pg= (g * a)/255;
+        int pb= (b * a)/255;
+        return uint((a<<24) | (pr<<16) | (pg<<8) | (pb));
+    }
+public:
+    ushort a;
+    ushort r;
+    ushort g;
+    ushort b;
+};
+
+enum class FillRule {
+    EvenOdd,
+    Winding
+};
+
+enum class JoinStyle {
+    Miter,
+    Bevel,
+    Round
+};
+enum class CapStyle {
+    Flat,
+    Square,
+    Round
+};
+
+
+#ifndef V_CONSTRUCTOR_FUNCTION
+# define V_CONSTRUCTOR_FUNCTION0(AFUNC) \
+    namespace { \
+    static const struct AFUNC ## _ctor_class_ { \
+        inline AFUNC ## _ctor_class_() { AFUNC(); } \
+    } AFUNC ## _ctor_instance_; \
+    }
+
+# define V_CONSTRUCTOR_FUNCTION(AFUNC) V_CONSTRUCTOR_FUNCTION0(AFUNC)
+#endif
+
+#endif //VGLOBAL_H
diff --git a/src/vector/vinterpolator.cpp b/src/vector/vinterpolator.cpp
new file mode 100644 (file)
index 0000000..5c285f6
--- /dev/null
@@ -0,0 +1,130 @@
+#include"vinterpolator.h"
+#include<cmath>
+
+#define NEWTON_ITERATIONS          4
+#define NEWTON_MIN_SLOPE           0.02
+#define SUBDIVISION_PRECISION      0.0000001
+#define SUBDIVISION_MAX_ITERATIONS 10
+
+const float VInterpolator::kSampleStepSize =
+                                        1.0 / float(VInterpolator::kSplineTableSize - 1);
+
+void
+VInterpolator::init(float aX1, float aY1, float aX2, float aY2)
+{
+    mX1 = aX1;
+    mY1 = aY1;
+    mX2 = aX2;
+    mY2 = aY2;
+
+    if (mX1 != mY1 || mX2 != mY2)
+      CalcSampleValues();
+}
+
+/*static*/ float
+VInterpolator::CalcBezier(float aT,
+                           float aA1,
+                           float aA2)
+{
+  // use Horner's scheme to evaluate the Bezier polynomial
+  return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
+}
+
+void
+VInterpolator::CalcSampleValues()
+{
+    for (int i = 0; i < kSplineTableSize; ++i) {
+        mSampleValues[i] = CalcBezier(float(i) * kSampleStepSize, mX1, mX2);
+    }
+}
+
+float
+VInterpolator::GetSlope(float aT,
+                         float aA1,
+                         float aA2)
+{
+  return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
+}
+
+
+float
+VInterpolator::value(float aX) const
+{
+  if (mX1 == mY1 && mX2 == mY2)
+    return aX;
+
+  return CalcBezier(GetTForX(aX), mY1, mY2);
+}
+
+float
+VInterpolator::GetTForX(float aX) const
+{
+  // Find interval where t lies
+  float intervalStart = 0.0;
+  const float* currentSample = &mSampleValues[1];
+  const float* const lastSample = &mSampleValues[kSplineTableSize - 1];
+  for (; currentSample != lastSample && *currentSample <= aX;
+        ++currentSample) {
+    intervalStart += kSampleStepSize;
+  }
+  --currentSample; // t now lies between *currentSample and *currentSample+1
+
+  // Interpolate to provide an initial guess for t
+  float dist = (aX - *currentSample) /
+                (*(currentSample+1) - *currentSample);
+  float guessForT = intervalStart + dist * kSampleStepSize;
+
+  // Check the slope to see what strategy to use. If the slope is too small
+  // Newton-Raphson iteration won't converge on a root so we use bisection
+  // instead.
+  float initialSlope = GetSlope(guessForT, mX1, mX2);
+  if (initialSlope >= NEWTON_MIN_SLOPE) {
+    return NewtonRaphsonIterate(aX, guessForT);
+  } else if (initialSlope == 0.0) {
+    return guessForT;
+  } else {
+    return BinarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
+  }
+}
+
+float
+VInterpolator::NewtonRaphsonIterate(float aX, float aGuessT) const
+{
+  // Refine guess with Newton-Raphson iteration
+  for (int i = 0; i < NEWTON_ITERATIONS; ++i) {
+    // We're trying to find where f(t) = aX,
+    // so we're actually looking for a root for: CalcBezier(t) - aX
+    float currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
+    float currentSlope = GetSlope(aGuessT, mX1, mX2);
+
+    if (currentSlope == 0.0)
+      return aGuessT;
+
+    aGuessT -= currentX / currentSlope;
+  }
+
+  return aGuessT;
+}
+
+float
+VInterpolator::BinarySubdivide(float aX, float aA, float aB) const
+{
+  float currentX;
+  float currentT;
+  int i = 0;
+
+  do
+  {
+    currentT = aA + (aB - aA) / 2.0;
+    currentX = CalcBezier(currentT, mX1, mX2) - aX;
+
+    if (currentX > 0.0) {
+      aB = currentT;
+    } else {
+      aA = currentT;
+    }
+  } while (fabs(currentX) > SUBDIVISION_PRECISION
+           && ++i < SUBDIVISION_MAX_ITERATIONS);
+
+  return currentT;
+}
diff --git a/src/vector/vinterpolator.h b/src/vector/vinterpolator.h
new file mode 100644 (file)
index 0000000..23c78e8
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef VINTERPOLATOR_H
+#define VINTERPOLATOR_H
+
+#include "vpoint.h"
+
+class VInterpolator
+{
+public:
+    VInterpolator() { /* caller must call Init later */ }
+
+    VInterpolator(float aX1, float aY1,
+                    float aX2, float aY2)
+    {
+      init(aX1, aY1, aX2, aY2);
+    }
+
+    VInterpolator(VPointF pt1, VPointF pt2)
+    {
+      init(pt1.x(), pt1.y(), pt2.x(), pt2.y());
+    }
+
+    void init(float aX1, float aY1,
+              float aX2, float aY2);
+
+    float value(float aX) const;
+
+    void GetSplineDerivativeValues(float aX, float& aDX, float& aDY) const;
+private:
+    void
+      CalcSampleValues();
+
+      /**
+       * Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
+       */
+      static float
+      CalcBezier(float aT, float aA1, float aA2);
+
+      /**
+       * Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
+       */
+      static float
+      GetSlope(float aT, float aA1, float aA2);
+
+      float
+      GetTForX(float aX) const;
+
+      float
+      NewtonRaphsonIterate(float aX, float aGuessT) const;
+
+      float
+      BinarySubdivide(float aX, float aA, float aB) const;
+
+      static float
+      A(float aA1, float aA2)
+      {
+        return 1.0 - 3.0 * aA2 + 3.0 * aA1;
+      }
+
+      static float
+      B(float aA1, float aA2)
+      {
+        return 3.0 * aA2 - 6.0 * aA1;
+      }
+
+      static float
+      C(float aA1)
+      {
+        return 3.0 * aA1;
+      }
+
+    float mX1;
+    float mY1;
+    float mX2;
+    float mY2;
+    enum { kSplineTableSize = 11 };
+    float mSampleValues[kSplineTableSize];
+    static const float kSampleStepSize;
+};
+#endif // VINTERPOLATOR_H
diff --git a/src/vector/vmatrix.cpp b/src/vector/vmatrix.cpp
new file mode 100644 (file)
index 0000000..9f47484
--- /dev/null
@@ -0,0 +1,835 @@
+#include"vmatrix.h"
+#include<cmath>
+#include<cstring>
+#include<cassert>
+#include<vglobal.h>
+
+/*  m11  m21  mtx
+ *  m12  m22  mty
+ *  m13  m23  m33
+ */
+
+struct VMatrixData {
+    RefCount             ref;
+    VMatrix::MatrixType type;
+    VMatrix::MatrixType dirty;
+    float m11, m12, m13;
+    float m21, m22, m23;
+    float mtx, mty, m33;
+};
+static const struct VMatrixData shared_empty = {RefCount(-1),
+                                                 VMatrix::MatrixType::None,
+                                                 VMatrix::MatrixType::None,
+                                                 1, 0, 0,
+                                                 0, 1, 0,
+                                                 0, 0, 1};
+inline float VMatrix::determinant() const
+{
+    return d->m11*(d->m33*d->m22 - d->mty*d->m23) -
+           d->m21*(d->m33*d->m12 - d->mty*d->m13)+d->mtx*(d->m23*d->m12 - d->m22*d->m13);
+}
+
+bool VMatrix::isAffine() const
+{
+    return type() < MatrixType::Project;
+}
+
+bool VMatrix::isIdentity() const
+{
+    return type() == MatrixType::None;
+}
+
+bool VMatrix::isInvertible() const
+{
+    return !vIsNull(determinant());
+}
+
+bool VMatrix::isScaling() const
+{
+    return type() >= MatrixType::Scale;
+}
+bool VMatrix::isRotating() const
+{
+    return type() >= MatrixType::Rotate;
+}
+
+bool VMatrix::isTranslating() const
+{
+    return type() >= MatrixType::Translate;
+}
+
+inline void VMatrix::cleanUp(VMatrixData *d)
+{
+    delete d;
+}
+
+void VMatrix::detach()
+{
+    if (d->ref.isShared())
+        *this = copy();
+}
+
+VMatrix VMatrix::copy() const
+{
+    VMatrix r;
+
+    r.d = new VMatrixData;
+    memcpy(r.d, d, sizeof(VMatrixData));
+    r.d->ref.setOwned();
+    return r;
+}
+
+VMatrix::VMatrix()
+    : d(const_cast<VMatrixData*>(&shared_empty))
+{
+}
+
+VMatrix::~VMatrix()
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+}
+
+VMatrix::VMatrix(bool init V_UNUSED)
+{
+    d = new VMatrixData;
+    memcpy(d, &shared_empty, sizeof(VMatrixData));
+    d->ref.setOwned();
+}
+
+VMatrix::VMatrix(float h11, float h12, float h13,
+                   float h21, float h22, float h23,
+                   float h31, float h32, float h33)
+{
+    d = new VMatrixData;
+    d->ref.setOwned();
+    d->m11 = h11; d->m12 = h12; d->m13 = h13;
+    d->m21 = h21; d->m22 = h22; d->m23 = h23;
+    d->mtx = h31; d->mty = h32; d->m33 = h33;
+    d->type = MatrixType::None;
+    d->dirty = MatrixType::Project;
+}
+
+VMatrix::VMatrix(const VMatrix &m)
+{
+    d = m.d;
+    d->ref.ref();
+}
+
+VMatrix::VMatrix(VMatrix &&other): d(other.d)
+{
+    other.d = const_cast<VMatrixData*>(&shared_empty);
+}
+
+VMatrix &VMatrix::operator=(const VMatrix &m)
+{
+    m.d->ref.ref();
+    if (!d->ref.deref())
+        cleanUp(d);
+
+    d = m.d;
+    return *this;
+}
+
+inline VMatrix &VMatrix::operator=(VMatrix &&other)
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+    d = other.d;
+    other.d = const_cast<VMatrixData*>(&shared_empty);
+    return *this;
+}
+
+inline VMatrix &VMatrix::operator*=(float num)
+{
+    if (num == 1.)
+        return *this;
+    detach();
+    d->m11 *= num;
+    d->m12 *= num;
+    d->m13 *= num;
+    d->m21 *= num;
+    d->m22 *= num;
+    d->m23 *= num;
+    d->mtx *= num;
+    d->mty *= num;
+    d->m33 *= num;
+    if (d->dirty < MatrixType::Scale)
+        d->dirty = MatrixType::Scale;
+
+    return *this;
+}
+
+inline VMatrix &VMatrix::operator/=(float div)
+{
+    if (div == 0)
+        return *this;
+    detach();
+    div = 1/div;
+    return operator*=(div);
+}
+
+VMatrix::MatrixType VMatrix::type() const
+{
+    if(d->dirty == MatrixType::None || d->dirty < d->type)
+        return static_cast<MatrixType>(d->type);
+
+    switch (static_cast<MatrixType>(d->dirty)) {
+    case MatrixType::Project:
+        if (!vIsNull(d->m13) || !vIsNull(d->m23) || !vIsNull(d->m33 - 1)) {
+             d->type = MatrixType::Project;
+             break;
+        }
+    case MatrixType::Shear:
+    case MatrixType::Rotate:
+        if (!vIsNull(d->m12) || !vIsNull(d->m21)) {
+            const float dot = d->m11 * d->m12 + d->m21 * d->m22;
+            if (vIsNull(dot))
+                d->type = MatrixType::Rotate;
+            else
+                d->type = MatrixType::Shear;
+            break;
+        }
+    case MatrixType::Scale:
+        if (!vIsNull(d->m11 - 1) || !vIsNull(d->m22 - 1)) {
+            d->type = MatrixType::Scale;
+            break;
+        }
+    case MatrixType::Translate:
+        if (!vIsNull(d->mtx) || !vIsNull(d->mty)) {
+            d->type = MatrixType::Translate;
+            break;
+        }
+    case MatrixType::None:
+        d->type = MatrixType::None;
+        break;
+    }
+
+    d->dirty = MatrixType::None;
+    return static_cast<MatrixType>(d->type);
+}
+
+
+VMatrix &VMatrix::translate(float dx, float dy)
+{
+    if (dx == 0 && dy == 0)
+        return *this;
+    detach();
+    switch(type()) {
+    case MatrixType::None:
+        d->mtx = dx;
+        d->mty = dy;
+        break;
+    case MatrixType::Translate:
+        d->mtx += dx;
+        d->mty += dy;
+        break;
+    case MatrixType::Scale:
+        d->mtx += dx* d->m11;
+        d->mty += dy* d->m22;
+        break;
+    case MatrixType::Project:
+        d->m33 += dx * d->m13 + dy * d->m23;
+    case MatrixType::Shear:
+    case MatrixType::Rotate:
+        d->mtx += dx*d->m11 + dy*d->m21;
+        d->mty += dy*d->m22 + dx*d->m12;
+        break;
+    }
+    if (d->dirty < MatrixType::Translate)
+        d->dirty = MatrixType::Translate;
+    return *this;
+}
+
+VMatrix & VMatrix::scale(float sx, float sy)
+{
+    if (sx == 1 && sy == 1)
+        return *this;
+    detach();
+    switch(type()) {
+    case MatrixType::None:
+    case MatrixType::Translate:
+        d->m11 = sx;
+        d->m22 = sy;
+        break;
+    case MatrixType::Project:
+        d->m13 *= sx;
+        d->m23 *= sy;
+    case MatrixType::Rotate:
+    case MatrixType::Shear:
+        d->m12 *= sx;
+        d->m21 *= sy;
+    case MatrixType::Scale:
+        d->m11 *= sx;
+        d->m22 *= sy;
+        break;
+    }
+    if (d->dirty < MatrixType::Scale)
+        d->dirty =  MatrixType::Scale;
+    return *this;
+}
+
+VMatrix & VMatrix::shear(float sh, float sv)
+{
+    if (sh == 0 && sv == 0)
+        return *this;
+    detach();
+    switch(type()) {
+    case MatrixType::None:
+    case MatrixType::Translate:
+        d->m12 = sv;
+        d->m21 = sh;
+        break;
+    case MatrixType::Scale:
+        d->m12 = sv*d->m22;
+        d->m21 = sh*d->m11;
+        break;
+    case MatrixType::Project: {
+        float tm13 = sv*d->m23;
+        float tm23 = sh*d->m13;
+        d->m13 += tm13;
+        d->m23 += tm23;
+    }
+    case MatrixType::Rotate:
+    case MatrixType::Shear: {
+        float tm11 = sv*d->m21;
+        float tm22 = sh*d->m12;
+        float tm12 = sv*d->m22;
+        float tm21 = sh*d->m11;
+        d->m11 += tm11; d->m12 += tm12;
+        d->m21 += tm21; d->m22 += tm22;
+        break;
+    }
+    }
+    if (d->dirty < MatrixType::Shear)
+        d->dirty = MatrixType::Shear;
+    return *this;
+}
+
+
+static const float deg2rad = float(0.017453292519943295769);  // pi/180
+static const float inv_dist_to_plane = 1. / 1024.;
+
+VMatrix & VMatrix::rotate(float a, Axis axis)
+{
+    if (a == 0)
+        return *this;
+    detach();
+    float sina = 0;
+    float cosa = 0;
+    if (a == 90. || a == -270.)
+        sina = 1.;
+    else if (a == 270. || a == -90.)
+        sina = -1.;
+    else if (a == 180.)
+        cosa = -1.;
+    else{
+        float b = deg2rad*a;          // convert to radians
+        sina = std::sin(b);               // fast and convenient
+        cosa = std::cos(b);
+    }
+
+    if (axis == Axis::Z) {
+        switch(type()) {
+        case MatrixType::None:
+        case MatrixType::Translate:
+            d->m11 = cosa;
+            d->m12 = sina;
+            d->m21 = -sina;
+            d->m22 = cosa;
+            break;
+        case MatrixType::Scale: {
+            float tm11 = cosa*d->m11;
+            float tm12 = sina*d->m22;
+            float tm21 = -sina*d->m11;
+            float tm22 = cosa*d->m22;
+            d->m11 = tm11; d->m12 = tm12;
+            d->m21 = tm21; d->m22 = tm22;
+            break;
+        }
+        case MatrixType::Project: {
+            float tm13 = cosa*d->m13 + sina*d->m23;
+            float tm23 = -sina*d->m13 + cosa*d->m23;
+            d->m13 = tm13;
+            d->m23 = tm23;
+        }
+        case MatrixType::Rotate:
+        case MatrixType::Shear: {
+            float tm11 = cosa*d->m11 + sina*d->m21;
+            float tm12 = cosa*d->m12 + sina*d->m22;
+            float tm21 = -sina*d->m11 + cosa*d->m21;
+            float tm22 = -sina*d->m12 + cosa*d->m22;
+            d->m11 = tm11; d->m12 = tm12;
+            d->m21 = tm21; d->m22 = tm22;
+            break;
+        }
+        }
+        if (d->dirty < MatrixType::Rotate)
+            d->dirty = MatrixType::Rotate;
+    } else {
+        VMatrix result;
+        if (axis == Axis::Y) {
+            result.d->m11 = cosa;
+            result.d->m13 = -sina * inv_dist_to_plane;
+        } else {
+            result.d->m22 = cosa;
+            result.d->m23 = -sina * inv_dist_to_plane;
+        }
+        result.d->type = MatrixType::Project;
+        *this = result * *this;
+    }
+
+    return *this;
+}
+
+VMatrix VMatrix::operator*(const VMatrix &m) const
+{
+    const MatrixType otherType = m.type();
+    if (otherType == MatrixType::None)
+        return *this;
+
+    const MatrixType thisType = type();
+    if (thisType == MatrixType::None)
+        return m;
+
+    VMatrix t(true);
+    MatrixType type = vMax(thisType, otherType);
+    switch(type) {
+    case MatrixType::None:
+        break;
+    case MatrixType::Translate:
+        t.d->mtx = d->mtx + m.d->mtx;
+        t.d->mty += d->mty + m.d->mty;
+        break;
+    case MatrixType::Scale:
+    {
+        float m11 = d->m11*m.d->m11;
+        float m22 = d->m22*m.d->m22;
+
+        float m31 = d->mtx*m.d->m11 + m.d->mtx;
+        float m32 = d->mty*m.d->m22 + m.d->mty;
+
+        t.d->m11 = m11;
+        t.d->m22 = m22;
+        t.d->mtx = m31; t.d->mty = m32;
+        break;
+    }
+    case MatrixType::Rotate:
+    case MatrixType::Shear:
+    {
+        float m11 = d->m11*m.d->m11 + d->m12*m.d->m21;
+        float m12 = d->m11*m.d->m12 + d->m12*m.d->m22;
+
+        float m21 = d->m21*m.d->m11 + d->m22*m.d->m21;
+        float m22 = d->m21*m.d->m12 + d->m22*m.d->m22;
+
+        float m31 = d->mtx*m.d->m11 + d->mty*m.d->m21 + m.d->mtx;
+        float m32 = d->mtx*m.d->m12 + d->mty*m.d->m22 + m.d->mty;
+
+        t.d->m11 = m11; t.d->m12 = m12;
+        t.d->m21 = m21; t.d->m22 = m22;
+        t.d->mtx = m31; t.d->mty = m32;
+        break;
+    }
+    case MatrixType::Project:
+    {
+        float m11 = d->m11*m.d->m11 + d->m12*m.d->m21 + d->m13*m.d->mtx;
+        float m12 = d->m11*m.d->m12 + d->m12*m.d->m22 + d->m13*m.d->mty;
+        float m13 = d->m11*m.d->m13 + d->m12*m.d->m23 + d->m13*m.d->m33;
+
+        float m21 = d->m21*m.d->m11 + d->m22*m.d->m21 + d->m23*m.d->mtx;
+        float m22 = d->m21*m.d->m12 + d->m22*m.d->m22 + d->m23*m.d->mty;
+        float m23 = d->m21*m.d->m13 + d->m22*m.d->m23 + d->m23*m.d->m33;
+
+        float m31 = d->mtx*m.d->m11 + d->mty*m.d->m21 + d->m33*m.d->mtx;
+        float m32 = d->mtx*m.d->m12 + d->mty*m.d->m22 + d->m33*m.d->mty;
+        float m33 = d->mtx*m.d->m13 + d->mty*m.d->m23 + d->m33*m.d->m33;
+
+        t.d->m11 = m11; t.d->m12 = m12; t.d->m13 = m13;
+        t.d->m21 = m21; t.d->m22 = m22; t.d->m23 = m23;
+        t.d->mtx = m31; t.d->mty = m32; t.d->m33 = m33;
+    }
+    }
+
+    t.d->dirty = type;
+    t.d->type = type;
+
+    return t;
+}
+
+VMatrix & VMatrix::operator*=(const VMatrix &o)
+{
+    const MatrixType otherType = o.type();
+    if (otherType == MatrixType::None)
+        return *this;
+
+    const MatrixType thisType = type();
+    if (thisType == MatrixType::None)
+        return operator=(o);
+    detach();
+    MatrixType t = vMax(thisType, otherType);
+    switch(t) {
+    case MatrixType::None:
+        break;
+    case MatrixType::Translate:
+        d->mtx += o.d->mtx;
+        d->mty += o.d->mty;
+        break;
+    case MatrixType::Scale:
+    {
+        float m11 = d->m11*o.d->m11;
+        float m22 = d->m22*o.d->m22;
+
+        float m31 = d->mtx*o.d->m11 + o.d->mtx;
+        float m32 = d->mty*o.d->m22 + o.d->mty;
+
+        d->m11 = m11;
+        d->m22 = m22;
+        d->mtx = m31; d->mty = m32;
+        break;
+    }
+    case MatrixType::Rotate:
+    case MatrixType::Shear:
+    {
+        float m11 = d->m11*o.d->m11 + d->m12*o.d->m21;
+        float m12 = d->m11*o.d->m12 + d->m12*o.d->m22;
+
+        float m21 = d->m21*o.d->m11 + d->m22*o.d->m21;
+        float m22 = d->m21*o.d->m12 + d->m22*o.d->m22;
+
+        float m31 = d->mtx*o.d->m11 + d->mty*o.d->m21 + o.d->mtx;
+        float m32 = d->mtx*o.d->m12 + d->mty*o.d->m22 + o.d->mty;
+
+        d->m11 = m11; d->m12 = m12;
+        d->m21 = m21; d->m22 = m22;
+        d->mtx = m31; d->mty = m32;
+        break;
+    }
+    case MatrixType::Project:
+    {
+        float m11 = d->m11*o.d->m11 + d->m12*o.d->m21 + d->m13*o.d->mtx;
+        float m12 = d->m11*o.d->m12 + d->m12*o.d->m22 + d->m13*o.d->mty;
+        float m13 = d->m11*o.d->m13 + d->m12*o.d->m23 + d->m13*o.d->m33;
+
+        float m21 = d->m21*o.d->m11 + d->m22*o.d->m21 + d->m23*o.d->mtx;
+        float m22 = d->m21*o.d->m12 + d->m22*o.d->m22 + d->m23*o.d->mty;
+        float m23 = d->m21*o.d->m13 + d->m22*o.d->m23 + d->m23*o.d->m33;
+
+        float m31 = d->mtx*o.d->m11 + d->mty*o.d->m21 + d->m33*o.d->mtx;
+        float m32 = d->mtx*o.d->m12 + d->mty*o.d->m22 + d->m33*o.d->mty;
+        float m33 = d->mtx*o.d->m13 + d->mty*o.d->m23 + d->m33*o.d->m33;
+
+        d->m11 = m11; d->m12 = m12; d->m13 = m13;
+        d->m21 = m21; d->m22 = m22; d->m23 = m23;
+        d->mtx = m31; d->mty = m32; d->m33 = m33;
+    }
+    }
+
+    d->dirty = t;
+    d->type = t;
+
+    return *this;
+}
+
+VMatrix VMatrix::adjoint() const
+{
+    float h11, h12, h13,
+        h21, h22, h23,
+        h31, h32, h33;
+    h11 = d->m22*d->m33 - d->m23*d->mty;
+    h21 = d->m23*d->mtx - d->m21*d->m33;
+    h31 = d->m21*d->mty - d->m22*d->mtx;
+    h12 = d->m13*d->mty - d->m12*d->m33;
+    h22 = d->m11*d->m33 - d->m13*d->mtx;
+    h32 = d->m12*d->mtx - d->m11*d->mty;
+    h13 = d->m12*d->m23 - d->m13*d->m22;
+    h23 = d->m13*d->m21 - d->m11*d->m23;
+    h33 = d->m11*d->m22 - d->m12*d->m21;
+
+    return VMatrix(h11, h12, h13,
+                      h21, h22, h23,
+                      h31, h32, h33);
+}
+
+VMatrix VMatrix::inverted(bool *invertible) const
+{
+    VMatrix invert(true);
+    bool inv = true;
+
+    switch(type()) {
+    case MatrixType::None:
+        break;
+    case MatrixType::Translate:
+        invert.d->mtx = -d->mtx;
+        invert.d->mty = -d->mty;
+        break;
+    case MatrixType::Scale:
+        inv = !vIsNull(d->m11);
+        inv &= !vIsNull(d->m22);
+        if (inv) {
+            invert.d->m11 = 1. / d->m11;
+            invert.d->m22 = 1. / d->m22;
+            invert.d->mtx = -d->mtx * invert.d->m11;
+            invert.d->mty = -d->mty * invert.d->m22;
+        }
+        break;
+    default:
+        // general case
+        float det = determinant();
+        inv = !vIsNull(det);
+        if (inv)
+            invert = (adjoint() /= det);
+        //TODO Test above line
+        break;
+    }
+
+    if (invertible)
+        *invertible = inv;
+
+    if (inv) {
+        // inverting doesn't change the type
+        invert.d->type = d->type;
+        invert.d->dirty = d->dirty;
+    }
+
+    return invert;
+}
+
+bool VMatrix::operator==(const VMatrix &o) const
+{
+    if (d == o.d) return true;
+
+    return d->m11 == o.d->m11 &&
+           d->m12 == o.d->m12 &&
+           d->m13 == o.d->m13 &&
+           d->m21 == o.d->m21 &&
+           d->m22 == o.d->m22 &&
+           d->m23 == o.d->m23 &&
+           d->mtx == o.d->mtx &&
+           d->mty == o.d->mty &&
+           d->m33 == o.d->m33;
+}
+
+bool VMatrix::operator!=(const VMatrix &o) const
+{
+    return !operator==(o);
+}
+
+bool VMatrix::fuzzyCompare(const VMatrix& o) const
+{
+    if (*this == o) return true;
+    return vCompare(d->m11 , o.d->m11 )
+        && vCompare(d->m12 , o.d->m12)
+        && vCompare(d->m21 , o.d->m21)
+        && vCompare(d->m22 , o.d->m22)
+        && vCompare(d->mtx , o.d->mtx)
+        && vCompare(d->mty , o.d->mty);
+}
+
+#define V_NEAR_CLIP 0.000001
+#ifdef MAP
+#  undef MAP
+#endif
+#define MAP(x, y, nx, ny) \
+    do { \
+        float FX_ = x; \
+        float FY_ = y; \
+        switch(t) {   \
+        case MatrixType::None:  \
+            nx = FX_;   \
+            ny = FY_;   \
+            break;    \
+        case MatrixType::Translate:    \
+            nx = FX_ + d->mtx;                \
+            ny = FY_ + d->mty;                \
+            break;                              \
+        case MatrixType::Scale:                           \
+            nx = d->m11 * FX_ + d->mtx;  \
+            ny = d->m22 * FY_ + d->mty;  \
+            break;                              \
+        case MatrixType::Rotate:                          \
+        case MatrixType::Shear:                           \
+        case MatrixType::Project:                                      \
+            nx = d->m11 * FX_ + d->m21 * FY_ + d->mtx;        \
+            ny = d->m12 * FX_ + d->m22 * FY_ + d->mty;        \
+            if (t == MatrixType::Project) {                                       \
+                float w = ( d->m13 * FX_ + d->m23 * FY_ + d->m33);              \
+                if (w < V_NEAR_CLIP) w = V_NEAR_CLIP;     \
+                w = 1./w;                                               \
+                nx *= w;                                                \
+                ny *= w;                                                \
+            }                                                           \
+        }                                                               \
+    } while (0)
+
+VRect VMatrix::map(const VRect &rect) const
+{
+    VMatrix::MatrixType t = type();
+    if (t <= MatrixType::Translate)
+        return rect.translated(std::round(d->mtx), std::round(d->mty));
+
+    if (t <= MatrixType::Scale) {
+        int x = std::round(d->m11*rect.x() + d->mtx);
+        int y = std::round(d->m22*rect.y() + d->mty);
+        int w = std::round(d->m11*rect.width());
+        int h = std::round(d->m22*rect.height());
+        if (w < 0) {
+            w = -w;
+            x -= w;
+        }
+        if (h < 0) {
+            h = -h;
+            y -= h;
+        }
+        return VRect(x, y, w, h);
+    } else if (t < MatrixType::Project) {
+        // see mapToPolygon for explanations of the algorithm.
+        float x = 0, y = 0;
+        MAP(rect.left(), rect.top(), x, y);
+        float xmin = x;
+        float ymin = y;
+        float xmax = x;
+        float ymax = y;
+        MAP(rect.right() + 1, rect.top(), x, y);
+        xmin = vMin(xmin, x);
+        ymin = vMin(ymin, y);
+        xmax = vMax(xmax, x);
+        ymax = vMax(ymax, y);
+        MAP(rect.right() + 1, rect.bottom() + 1, x, y);
+        xmin = vMin(xmin, x);
+        ymin = vMin(ymin, y);
+        xmax = vMax(xmax, x);
+        ymax = vMax(ymax, y);
+        MAP(rect.left(), rect.bottom() + 1, x, y);
+        xmin = vMin(xmin, x);
+        ymin = vMin(ymin, y);
+        xmax = vMax(xmax, x);
+        ymax = vMax(ymax, y);
+        return VRect(std::round(xmin), std::round(ymin), std::round(xmax)-std::round(xmin), std::round(ymax)-std::round(ymin));
+    } else {
+        // Not supported
+        assert(0);
+    }
+}
+
+VRegion VMatrix::map(const VRegion &r) const
+{
+    VMatrix::MatrixType t = type();
+    if (t == MatrixType::None)
+        return r;
+
+    if (t == MatrixType::Translate) {
+        VRegion copy(r);
+        copy.translate(std::round(d->mtx), std::round(d->mty));
+        return copy;
+    }
+
+    if (t == MatrixType::Scale && r.rectCount() == 1)
+        return VRegion(map(r.boundingRect()));
+    // handle mapping of region properly
+    assert(0);
+    return r;
+}
+
+VPointF VMatrix::map(const VPointF &p) const
+{
+    float fx = p.x();
+    float fy = p.y();
+
+    float x = 0, y = 0;
+
+    VMatrix::MatrixType t = type();
+    switch(t) {
+    case MatrixType::None:
+        x = fx;
+        y = fy;
+        break;
+    case MatrixType::Translate:
+        x = fx + d->mtx;
+        y = fy + d->mty;
+        break;
+    case MatrixType::Scale:
+        x = d->m11 * fx + d->mtx;
+        y = d->m22 * fy + d->mty;
+        break;
+    case MatrixType::Rotate:
+    case MatrixType::Shear:
+    case MatrixType::Project:
+        x = d->m11 * fx + d->m21 * fy + d->mtx;
+        y = d->m12 * fx + d->m22 * fy + d->mty;
+        if (t == MatrixType::Project) {
+            float w = 1./(d->m13 * fx + d->m23 * fy + d->m33);
+            x *= w;
+            y *= w;
+        }
+    }
+    return VPointF(x, y);
+}
+static std::string type_helper(VMatrix::MatrixType t)
+{
+    switch(t) {
+    case VMatrix::MatrixType::None:
+        return "MatrixType::None";
+        break;
+    case VMatrix::MatrixType::Translate:
+        return "MatrixType::Translate";
+        break;
+    case VMatrix::MatrixType::Scale:
+        return "MatrixType::Scale";
+        break;
+    case VMatrix::MatrixType::Rotate:
+        return "MatrixType::Rotate";
+        break;
+    case VMatrix::MatrixType::Shear:
+        return "MatrixType::Shear";
+        break;
+    case VMatrix::MatrixType::Project:
+        return "MatrixType::Project";
+        break;
+    }
+    return "";
+}
+std::ostream& operator<<(std::ostream& os, const VMatrix& o)
+{
+    os<<"[Matrix: [dptr = "<<o.d<<"]"<<"[ref = "<<o.d->ref.count()<<"]"<<"type ="<<type_helper(o.type())<<", Data : "<<o.d->m11<<" "<<o.d->m12<<" "<<o.d->m13<<" "<<o.d->m21<<" "<<o.d->m22<<" "<<o.d->m23<<" "<<o.d->mtx<<" "<<o.d->mty<<" "<<o.d->m33<<" "<<"]"<<std::endl;
+    return os;
+}
+
+float VMatrix::m11()const
+{
+    return d->m11;
+}
+float VMatrix::m12()const
+{
+    return d->m12;
+}
+float VMatrix::m13()const
+{
+    return d->m13;
+}
+float VMatrix::m21()const
+{
+    return d->m21;
+}
+float VMatrix::m22()const
+{
+    return d->m22;
+}
+float VMatrix::m23()const
+{
+    return d->m23;
+}
+float VMatrix::m31()const
+{
+    return d->mtx;
+}
+float VMatrix::m32()const
+{
+    return d->mty;
+}
+float VMatrix::m33()const
+{
+    return d->m33;
+}
+
+
diff --git a/src/vector/vmatrix.h b/src/vector/vmatrix.h
new file mode 100644 (file)
index 0000000..4e43b47
--- /dev/null
@@ -0,0 +1,92 @@
+#ifndef VMATRIX_H
+#define VMATRIX_H
+#include"vpoint.h"
+#include "vregion.h"
+#include "vglobal.h"
+
+struct VMatrixData;
+class  VMatrix
+{
+public:
+    enum class Axis {
+        X,
+        Y,
+        Z
+    };
+    enum class MatrixType {
+        None      = 0x00,
+        Translate = 0x01,
+        Scale     = 0x02,
+        Rotate    = 0x04,
+        Shear     = 0x08,
+        Project   = 0x10
+    };
+
+    VMatrix();
+    ~VMatrix();
+    VMatrix(const VMatrix &matrix);
+    VMatrix(VMatrix &&other);
+    VMatrix &operator=(const VMatrix &);
+    VMatrix &operator=(VMatrix &&other);
+
+    bool isAffine() const;
+    bool isIdentity() const;
+    bool isInvertible() const;
+    bool isScaling() const;
+    bool isRotating() const;
+    bool isTranslating() const;
+    MatrixType type() const;
+    inline float determinant() const;
+
+    VMatrix &translate(VPointF pos) { return translate(pos.x(), pos.y());};
+    VMatrix &translate(float dx, float dy);
+    VMatrix &scale(VPointF s){ return scale(s.x(), s.y());};
+    VMatrix &scale(float sx, float sy);
+    VMatrix &shear(float sh, float sv);
+    VMatrix &rotate(float a, Axis axis = VMatrix::Axis::Z);
+    VMatrix &rotateRadians(float a, Axis axis = VMatrix::Axis::Z);
+
+    VPointF map(const VPointF &p) const;
+    inline VPointF map(float x, float y) const;
+    VRect map(const VRect &r) const;
+    VRegion map(const VRegion &r) const;
+
+    V_REQUIRED_RESULT VMatrix inverted(bool *invertible = nullptr) const;
+    V_REQUIRED_RESULT VMatrix adjoint() const;
+
+    VMatrix operator*(const VMatrix &o) const;
+    VMatrix &operator*=(const VMatrix &);
+    VMatrix &operator*=(float mul);
+    VMatrix &operator/=(float div);
+    bool operator==(const VMatrix &) const;
+    bool operator!=(const VMatrix &) const;
+    bool fuzzyCompare(const VMatrix &) const;
+    friend std::ostream& operator<<(std::ostream& os, const VMatrix& o);
+
+    float m11()const;
+    float m12()const;
+    float m13()const;
+    float m21()const;
+    float m22()const;
+    float m23()const;
+    float m31()const;
+    float m32()const;
+    float m33()const;
+private:
+    explicit VMatrix(bool init);
+    explicit VMatrix(float m11, float m12, float m13,
+                      float m21, float m22, float m23,
+                      float m31, float m32, float m33);
+    VMatrix copy() const;
+    void detach();
+    void cleanUp(VMatrixData *x);
+
+    VMatrixData *d;
+};
+
+inline VPointF VMatrix::map(float x, float y) const
+{
+    return map(VPointF(x, y));
+}
+
+#endif // VMATRIX_H
diff --git a/src/vector/vpainter.cpp b/src/vector/vpainter.cpp
new file mode 100644 (file)
index 0000000..5d3f4eb
--- /dev/null
@@ -0,0 +1,64 @@
+#include"vpainter.h"
+#include"vdrawhelper.h"
+
+class VPainterImpl
+{
+public:
+    void drawRle(const VPoint &pos, const VRle &rle);
+public:
+    VRasterBuffer mBuffer;
+    VSpanData     mSpanData;
+};
+
+void VPainterImpl::drawRle(const VPoint &pos, const VRle &rle)
+{
+    if (rle.isEmpty()) return;
+    //mSpanData.updateSpanFunc();
+
+    if (!mSpanData.mUnclippedBlendFunc) return;
+
+    // apply clip if any
+    VRle final = rle.intersected(mSpanData.mSystemClip);
+
+    if (final.isEmpty()) return;
+
+    mSpanData.mUnclippedBlendFunc(final.size(), final.data(), &mSpanData);
+}
+
+VPainter::~VPainter()
+{
+    delete mImpl;
+}
+
+VPainter::VPainter()
+{
+    mImpl = new VPainterImpl;
+}
+
+VPainter::VPainter(VBitmap *buffer)
+{
+    mImpl = new VPainterImpl;
+    begin(buffer);
+}
+bool VPainter::begin(VBitmap *buffer)
+{
+    mImpl->mBuffer.prepare(buffer);
+    mImpl->mSpanData.init(&mImpl->mBuffer);
+    //TODO find a better api to clear the surface
+    mImpl->mBuffer.clear();
+    return true;
+}
+void VPainter::end()
+{
+
+}
+
+void VPainter::setBrush(const VBrush &brush)
+{
+    mImpl->mSpanData.setup(brush);
+}
+
+void VPainter::drawRle(const VPoint &pos, const VRle &rle)
+{
+    mImpl->drawRle(pos, rle);
+}
diff --git a/src/vector/vpainter.h b/src/vector/vpainter.h
new file mode 100644 (file)
index 0000000..740f732
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef VPAINTER_H
+#define VPAINTER_H
+
+#include"vpoint.h"
+#include"vrle.h"
+#include"vbrush.h"
+
+class VBitmap;
+class VPainterImpl;
+class VPainter {
+public:
+    enum CompositionMode{
+        CompModeSrc,
+        CompModeSrcOver
+    };
+    ~VPainter();
+    VPainter();
+    VPainter(VBitmap *buffer);
+    bool begin(VBitmap *buffer);
+    void end();
+    void setBrush(const VBrush &brush);
+    void drawRle(const VPoint &pos, const VRle &rle);
+private:
+    VPainterImpl  *mImpl;
+};
+
+#endif //VPAINTER_H
diff --git a/src/vector/vpath.cpp b/src/vector/vpath.cpp
new file mode 100644 (file)
index 0000000..60f4c92
--- /dev/null
@@ -0,0 +1,735 @@
+#include"vpath.h"
+#include<vector>
+#include<cassert>
+#include"vdebug.h"
+#include"vbezier.h"
+#include "vrect.h"
+
+struct VPathData
+{
+    void copy(VPathData *o);
+    void moveTo(const VPointF &pt);
+    void lineTo(const VPointF &pt);
+    void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
+    void close();
+    void reset();
+    void checkNewSegment();
+    int  segments() const;
+    void transform(const VMatrix &m);
+    RefCount                      ref;
+    std::vector<VPointF>         m_points;
+    std::vector<VPath::Element>  m_elements;
+    int                           m_segments;
+    VPointF                      mStartPoint;
+    bool                          mNewSegment;
+};
+
+
+void VPathData::transform(const VMatrix &m)
+{
+    for(auto &i : m_points) {
+        i = m.map(i);
+    }
+}
+
+void VPathData::checkNewSegment()
+{
+    if (mNewSegment) {
+        moveTo(VPointF(0,0));
+        mNewSegment = false;
+    }
+}
+void VPathData::copy(VPathData *o)
+{
+    m_points = o->m_points;
+    m_elements = o->m_elements;
+    m_segments = o->m_segments;
+    mStartPoint = o->mStartPoint;
+}
+
+void VPathData::moveTo(const VPointF &p)
+{
+    mStartPoint = p;
+    mNewSegment = false;
+    m_elements.push_back(VPath::Element::MoveTo);
+    m_points.push_back(p);
+    m_segments++;
+}
+void VPathData::lineTo(const VPointF &p)
+{
+    checkNewSegment();
+    m_elements.push_back(VPath::Element::LineTo);
+    m_points.push_back(p);
+}
+void VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
+{
+    checkNewSegment();
+    m_elements.push_back(VPath::Element::CubicTo);
+    m_points.push_back(c1);
+    m_points.push_back(c2);
+    m_points.push_back(e);
+}
+
+void VPathData::close()
+{
+    const VPointF &lastPt = m_points.back();
+    if (!fuzzyCompare(mStartPoint, lastPt)) {
+       lineTo(mStartPoint);
+    }
+    m_elements.push_back(VPath::Element::Close);
+    mNewSegment = true;
+}
+
+void VPathData::reset()
+{
+    m_elements.clear();
+    m_points.clear();
+    m_segments = 0;
+}
+
+int  VPathData::segments() const
+{
+    return m_segments;
+}
+
+
+static const struct VPathData shared_empty = {RefCount(-1),
+                                               std::vector<VPointF>(),
+                                               std::vector<VPath::Element>(),
+                                               0,
+                                               VPointF(),
+                                               true};
+
+inline void VPath::cleanUp(VPathData *d)
+{
+    delete d;
+}
+
+void VPath::detach()
+{
+    if (d->ref.isShared())
+        *this = copy();
+}
+
+VPath VPath::copy() const
+{
+    VPath other;
+
+    other.d = new VPathData(shared_empty);
+    other.d->m_points = d->m_points;
+    other.d->m_elements = d->m_elements;
+    other.d->m_segments = d->m_segments;
+    other.d->ref.setOwned();
+    return other;
+}
+
+VPath::~VPath()
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+}
+
+VPath::VPath()
+    : d(const_cast<VPathData*>(&shared_empty))
+{
+}
+
+VPath::VPath(const VPath &other)
+{
+    d = other.d;
+    d->ref.ref();
+}
+
+VPath::VPath(VPath &&other): d(other.d)
+{
+    other.d = const_cast<VPathData*>(&shared_empty);
+}
+
+VPath &VPath::operator=(const VPath &other)
+{
+    other.d->ref.ref();
+    if (!d->ref.deref())
+        cleanUp(d);
+
+    d = other.d;
+    return *this;
+}
+
+inline VPath &VPath::operator=(VPath &&other)
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+    d = other.d;
+    other.d = const_cast<VPathData*>(&shared_empty);
+    return *this;
+}
+
+bool VPath::isEmpty()const
+{
+    return d->m_elements.empty();
+}
+
+void VPath::close()
+{
+    if (isEmpty()) return;
+    detach();
+    d->close();
+}
+
+void VPath::reset()
+{
+    if (isEmpty()) return;
+    detach();
+    d->reset();
+}
+
+void VPath::moveTo(const VPointF &p)
+{
+    detach();
+    d->moveTo(p);
+}
+
+void VPath::lineTo(const VPointF &p)
+{
+    detach();
+    d->lineTo(p);
+}
+
+void VPath::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
+{
+    detach();
+    d->cubicTo(c1, c2, e);
+}
+
+void VPath::reserve(int num_elm)
+{
+    detach();
+    d->m_elements.reserve(num_elm);
+    d->m_points.reserve(num_elm);
+}
+
+const std::vector<VPath::Element> &VPath::elements() const
+{
+    return d->m_elements;
+}
+const std::vector<VPointF> &VPath::points() const
+{
+    return d->m_points;
+}
+int VPath::segments() const
+{
+    return d->m_segments + 1;
+}
+
+#define PATH_KAPPA 0.5522847498
+
+static float tForArcAngle(float angle);
+void findEllipseCoords(const VRectF &r, float angle, float length,
+                       VPointF* startPoint, VPointF *endPoint)
+{
+    if (r.isNull()) {
+        if (startPoint)
+            *startPoint = VPointF();
+        if (endPoint)
+            *endPoint = VPointF();
+        return;
+    }
+
+    float w2 = r.width() / 2;
+    float h2 = r.height() / 2;
+
+    float angles[2] = { angle, angle + length };
+    VPointF *points[2] = { startPoint, endPoint };
+
+    for (int i = 0; i < 2; ++i) {
+        if (!points[i])
+            continue;
+
+        float theta = angles[i] - 360 * floor(angles[i] / 360);
+        float t = theta / 90;
+        // truncate
+        int quadrant = int(t);
+        t -= quadrant;
+
+        t = tForArcAngle(90 * t);
+
+        // swap x and y?
+        if (quadrant & 1)
+            t = 1 - t;
+
+        float a, b, c, d;
+        VBezier::coefficients(t, a, b, c, d);
+        VPointF p(a + b + c*PATH_KAPPA, d + c + b*PATH_KAPPA);
+
+        // left quadrants
+        if (quadrant == 1 || quadrant == 2)
+            p.rx() = -p.x();
+
+        // top quadrants
+        if (quadrant == 0 || quadrant == 1)
+            p.ry() = -p.y();
+
+        *points[i] = r.center() + VPointF(w2 * p.x(), h2 * p.y());
+    }
+}
+
+static float
+tForArcAngle(float angle)
+{
+   float radians, cos_angle, sin_angle, tc, ts, t;
+
+   if (floatCmp(angle,0.f)) return 0;
+   if (floatCmp(angle, 90.0)) return 1;
+
+   radians = (angle/180) * M_PI;
+
+   cos_angle = cos(radians);
+   sin_angle = sin(radians);
+
+   // initial guess
+   tc = angle / 90;
+
+   // do some iterations of newton's method to approximate cos_angle
+   // finds the zero of the function b.pointAt(tc).x() - cos_angle
+   tc -= ((((2-3*PATH_KAPPA) * tc + 3*(PATH_KAPPA-1)) * tc) * tc + 1 - cos_angle) // value
+   / (((6-9*PATH_KAPPA) * tc + 6*(PATH_KAPPA-1)) * tc); // derivative
+   tc -= ((((2-3*PATH_KAPPA) * tc + 3*(PATH_KAPPA-1)) * tc) * tc + 1 - cos_angle) // value
+   / (((6-9*PATH_KAPPA) * tc + 6*(PATH_KAPPA-1)) * tc); // derivative
+
+   // initial guess
+   ts = tc;
+   // do some iterations of newton's method to approximate sin_angle
+   // finds the zero of the function b.pointAt(tc).y() - sin_angle
+   ts -= ((((3*PATH_KAPPA-2) * ts -  6*PATH_KAPPA + 3) * ts + 3*PATH_KAPPA) * ts - sin_angle)
+   / (((9*PATH_KAPPA-6) * ts + 12*PATH_KAPPA - 6) * ts + 3*PATH_KAPPA);
+   ts -= ((((3*PATH_KAPPA-2) * ts -  6*PATH_KAPPA + 3) * ts + 3*PATH_KAPPA) * ts - sin_angle)
+   / (((9*PATH_KAPPA-6) * ts + 12*PATH_KAPPA - 6) * ts + 3*PATH_KAPPA);
+
+   // use the average of the t that best approximates cos_angle
+   // and the t that best approximates sin_angle
+   t = 0.5 * (tc + ts);
+   return t;
+}
+
+
+// The return value is the starting point of the arc
+static VPointF
+curvesForArc(const VRectF &rect, float startAngle, float sweepLength,
+              VPointF *curves, int *point_count)
+{
+    if (rect.isNull()) {
+        return VPointF();
+    }
+
+    float x = rect.x();
+    float y = rect.y();
+
+    float w = rect.width();
+    float w2 = rect.width() / 2;
+    float w2k = w2 * PATH_KAPPA;
+
+    float h = rect.height();
+    float h2 = rect.height() / 2;
+    float h2k = h2 * PATH_KAPPA;
+
+    VPointF points[16] =
+    {
+        // start point
+        VPointF(x + w, y + h2),
+
+        // 0 -> 270 degrees
+        VPointF(x + w, y + h2 + h2k),
+        VPointF(x + w2 + w2k, y + h),
+        VPointF(x + w2, y + h),
+
+        // 270 -> 180 degrees
+        VPointF(x + w2 - w2k, y + h),
+        VPointF(x, y + h2 + h2k),
+        VPointF(x, y + h2),
+
+        // 180 -> 90 degrees
+        VPointF(x, y + h2 - h2k),
+        VPointF(x + w2 - w2k, y),
+        VPointF(x + w2, y),
+
+        // 90 -> 0 degrees
+        VPointF(x + w2 + w2k, y),
+        VPointF(x + w, y + h2 - h2k),
+        VPointF(x + w, y + h2)
+    };
+
+    if (sweepLength > 360) sweepLength = 360;
+    else if (sweepLength < -360) sweepLength = -360;
+
+    // Special case fast paths
+    if (startAngle == 0.0) {
+        if (sweepLength == 360.0) {
+            for (int i = 11; i >= 0; --i)
+                curves[(*point_count)++] = points[i];
+            return points[12];
+        } else if (sweepLength == -360.0) {
+            for (int i = 1; i <= 12; ++i)
+                curves[(*point_count)++] = points[i];
+            return points[0];
+        }
+    }
+
+    int startSegment = int(floor(startAngle / 90));
+    int endSegment = int(floor((startAngle + sweepLength) / 90));
+
+    float startT = (startAngle - startSegment * 90) / 90;
+    float endT = (startAngle + sweepLength - endSegment * 90) / 90;
+
+    int delta = sweepLength > 0 ? 1 : -1;
+    if (delta < 0) {
+        startT = 1 - startT;
+        endT = 1 - endT;
+    }
+
+    // avoid empty start segment
+    if (floatNull(startT - float(1))) {
+        startT = 0;
+        startSegment += delta;
+    }
+
+    // avoid empty end segment
+    if (floatNull(endT)) {
+        endT = 1;
+        endSegment -= delta;
+    }
+
+    startT = tForArcAngle(startT * 90);
+    endT = tForArcAngle(endT * 90);
+
+    const bool splitAtStart = !floatNull(startT);
+    const bool splitAtEnd = !floatNull(endT - float(1));
+
+    const int end = endSegment + delta;
+
+    // empty arc?
+    if (startSegment == end) {
+        const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
+        const int j = 3 * quadrant;
+        return delta > 0 ? points[j + 3] : points[j];
+    }
+
+    VPointF startPoint, endPoint;
+    findEllipseCoords(rect, startAngle, sweepLength, &startPoint, &endPoint);
+
+    for (int i = startSegment; i != end; i += delta) {
+        const int quadrant = 3 - ((i % 4) + 4) % 4;
+        const int j = 3 * quadrant;
+
+        VBezier b;
+        if (delta > 0)
+            b = VBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
+        else
+            b = VBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
+
+        // empty arc?
+        if (startSegment == endSegment && floatCmp(startT, endT))
+            return startPoint;
+
+        if (i == startSegment) {
+            if (i == endSegment && splitAtEnd)
+                b = b.onInterval(startT, endT);
+            else if (splitAtStart)
+                b = b.onInterval(startT, 1);
+        } else if (i == endSegment && splitAtEnd) {
+            b = b.onInterval(0, endT);
+        }
+
+        // push control points
+        curves[(*point_count)++] = b.pt2();
+        curves[(*point_count)++] = b.pt3();
+        curves[(*point_count)++] = b.pt4();
+    }
+
+    curves[*(point_count)-1] = endPoint;
+
+    return startPoint;
+}
+
+void VPath::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
+{
+    detach();
+
+    int point_count = 0;
+    VPointF pts[15];
+    VPointF curve_start = curvesForArc(rect, startAngle, sweepLength, pts, &point_count);
+
+    if (isEmpty() || forceMoveTo) {
+        d->moveTo(curve_start);
+    } else {
+        d->lineTo(curve_start);
+    }
+    for (int i=0; i<point_count; i+=3) {
+        d->cubicTo(pts[i], pts[i+1], pts[i+2]);
+    }
+}
+
+void VPath::addCircle(float cx, float cy, float radius, VPath::Direction dir)
+{
+    addOval(VRectF(cx-radius, cy-radius, 2*radius, 2*radius) , dir);
+}
+
+void VPath::addOval(const VRectF &rect, VPath::Direction dir)
+{
+    if (rect.isNull()) return;
+
+    detach();
+
+    float x = rect.x();
+    float y = rect.y();
+
+    float w = rect.width();
+    float w2 = rect.width() / 2;
+    float w2k = w2 * PATH_KAPPA;
+
+    float h = rect.height();
+    float h2 = rect.height() / 2;
+    float h2k = h2 * PATH_KAPPA;
+
+
+    if (dir == VPath::Direction::CW) {
+       // moveto 12 o'clock.
+       d->moveTo(VPointF(x+w2, y));
+       // 12 -> 3 o'clock
+       d->cubicTo(VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k), VPointF(x + w, y + h2));
+       // 3 -> 6 o'clock
+       d->cubicTo(VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h), VPointF(x + w2, y + h));
+       // 6 -> 9 o'clock
+       d->cubicTo(VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k), VPointF(x , y + h2));
+       // 9 -> 12 o'clock
+       d->cubicTo(VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y));
+    } else {
+       // moveto 12 o'clock.
+       d->moveTo(VPointF(x+w2, y));
+       // 12 -> 9 o'clock
+       d->cubicTo(VPointF(x + w2 - w2k, y), VPointF(x, y + h2 - h2k), VPointF(x , y + h2));
+       // 9 -> 6 o'clock
+       d->cubicTo(VPointF(x, y + h2 + h2k), VPointF(x + w2 - w2k, y + h), VPointF(x + w2, y + h));
+       // 6 -> 3 o'clock
+       d->cubicTo(VPointF(x + w2 + w2k, y + h), VPointF(x + w, y + h2 + h2k), VPointF(x + w, y + h2));
+       // 3 -> 12 o'clock
+       d->cubicTo(VPointF(x + w, y + h2 - h2k), VPointF(x + w2 + w2k, y), VPointF(x+w2, y));
+    }
+}
+
+void VPath::addRect(const VRectF &rect, VPath::Direction dir)
+{
+    if (rect.isNull()) return;
+
+    detach();
+
+    float x = rect.x();
+    float y = rect.y();
+    float w = rect.width();
+    float h = rect.height();
+
+    if (dir == VPath::Direction::CW) {
+      moveTo(VPointF(x + w, y));
+      lineTo(VPointF(x + w, y + h));
+      lineTo(VPointF(x , y + h));
+      lineTo(VPointF(x , y));
+      close();
+    } else {
+       moveTo(VPointF(x + w, y));
+       lineTo(VPointF(x , y));
+       lineTo(VPointF(x , y + h));
+       lineTo(VPointF(x + w, y + h));
+       close();
+    }
+}
+
+void VPath::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
+{
+    if (floatCmp(rx, 0.f) || floatCmp(ry, 0.f)) {
+        addRect(rect, dir);
+        return;
+    }
+
+    float x = rect.x();
+    float y = rect.y();
+    float w = rect.width();
+    float h = rect.height();
+    // clamp the rx and ry radius value.
+    rx = 2*rx;
+    ry = 2*ry;
+    if (rx > w) rx = w;
+    if (ry > h) ry = h;
+
+     if (dir == VPath::Direction::CW) {
+         moveTo(VPointF(x + w, y + ry/2.f));
+         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0 , -90, false);
+         arcTo(VRectF(x, y + h - ry, rx, ry), -90 , -90, false);
+         arcTo(VRectF(x, y, rx, ry), -180 , -90, false);
+         arcTo(VRectF(x + w - rx, y, rx, ry), -270 , -90, false);
+         close();
+     } else {
+         moveTo(VPointF(x + w, y + ry/2.f));
+         arcTo(VRectF(x + w - rx, y , rx, ry), 0 , 90, false);
+         arcTo(VRectF(x, y, rx, ry), 90 , 90, false);
+         arcTo(VRectF(x, y + h - ry, rx, ry), 180 , 90, false);
+         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270 , 90, false);
+         close();
+     }
+}
+
+void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
+                             float innerRadius, float outerRadius,
+                             float innerRoundness, float outerRoundness,
+                             VPath::Direction dir)
+{
+   // TODO: Direction feature is missing
+   const static float POLYSTAR_MAGIC_NUMBER = 0.47829 / 0.28;
+   float currentAngle = (startAngle - 90.0) * M_PI / 180.0;
+   float x;
+   float y;
+   float previousX;
+   float previousY;
+   float partialPointRadius = 0;
+   float anglePerPoint = (float) (2.0 * M_PI / points);
+   float halfAnglePerPoint = anglePerPoint / 2.0;
+   float partialPointAmount = points - (int) points;
+   bool longSegment = false;
+   int numPoints = (int) ceil(points) * 2.0;
+
+   innerRoundness /= 100.0;
+   outerRoundness /= 100.0;
+
+   if (partialPointAmount != 0) {
+        currentAngle += halfAnglePerPoint * (1.0 - partialPointAmount);
+   }
+
+   if (partialPointAmount != 0) {
+        partialPointRadius = innerRadius + partialPointAmount * (outerRadius - innerRadius);
+        x = (float) (partialPointRadius * cos(currentAngle));
+        y = (float) (partialPointRadius * sin(currentAngle));
+        currentAngle += anglePerPoint * partialPointAmount / 2.0;
+   } else {
+        x = (float) (outerRadius * cos(currentAngle));
+        y = (float) (outerRadius * sin(currentAngle));
+        currentAngle += halfAnglePerPoint;
+   }
+
+   moveTo(VPointF(x + cx, y + cy));
+
+   for (int i = 0; i < numPoints; i++) {
+        float radius = longSegment ? outerRadius : innerRadius;
+        float dTheta = halfAnglePerPoint;
+        if (partialPointRadius != 0 && i == numPoints - 2) {
+             dTheta = anglePerPoint * partialPointAmount / 2.0;
+        }
+        if (partialPointRadius != 0 && i == numPoints - 1) {
+             radius = partialPointRadius;
+        }
+        previousX = x;
+        previousY = y;
+        x = (float) (radius * cos(currentAngle));
+        y = (float) (radius * sin(currentAngle));
+
+        if (innerRoundness == 0 && outerRoundness == 0) {
+             lineTo(VPointF(x + cx, y + cy));
+        } else {
+             float cp1Theta = (float) (atan2(previousY, previousX) - M_PI / 2.0);
+             float cp1Dx = (float) cos(cp1Theta);
+             float cp1Dy = (float) sin(cp1Theta);
+
+             float cp2Theta = (float) (atan2(y, x) - M_PI / 2.0);
+             float cp2Dx = (float) cos(cp2Theta);
+             float cp2Dy = (float) sin(cp2Theta);
+
+             float cp1Roundness = longSegment ? innerRoundness : outerRoundness;
+             float cp2Roundness = longSegment ? outerRoundness : innerRoundness;
+             float cp1Radius = longSegment ? innerRadius : outerRadius;
+             float cp2Radius = longSegment ? outerRadius : innerRadius;
+
+             float cp1x = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER * cp1Dx / points;
+             float cp1y = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER * cp1Dy / points;
+             float cp2x = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER * cp2Dx / points;
+             float cp2y = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER * cp2Dy / points;
+
+             if ((partialPointAmount != 0) &&
+                 ((i == 0) || (i == numPoints - 1))) {
+                  cp1x *= partialPointAmount;
+                  cp1y *= partialPointAmount;
+                  cp2x *= partialPointAmount;
+                  cp2y *= partialPointAmount;
+             }
+
+             cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
+                     VPointF(x + cp2x + cx, y + cp2y + cy),
+                     VPointF(x + cx, y + cy));
+        }
+
+        currentAngle += dTheta;
+        longSegment = !longSegment;
+   }
+
+   close();
+}
+
+void VPath::addPolystarPolygon(float startAngle, float cx, float cy, float points,
+                                float radius, float roundness,
+                                VPath::Direction dir)
+{
+   // TODO: Direction feature is missing
+   // TODO: Need to support floating point number for number of points
+   const static float POLYGON_MAGIC_NUMBER = 0.25;
+   float currentAngle = (startAngle - 90.0) * M_PI / 180.0;
+   float x;
+   float y;
+   float previousX;
+   float previousY;
+   float anglePerPoint = (float) (2.0 * M_PI / floor(points));
+   int numPoints = (int) floor(points);
+
+   roundness /= 100.0;
+
+   currentAngle = (currentAngle - 90.0) * M_PI / 180.0;
+   x = (float) (radius * cos(currentAngle));
+   y = (float) (radius * sin(currentAngle));
+   currentAngle += anglePerPoint;
+
+   moveTo(VPointF(x + cx, y + cy));
+
+   for (int i = 0; i < numPoints; i++) {
+        previousX = x;
+        previousY = y;
+        x = (float) (radius * cos(currentAngle));
+        y = (float) (radius * sin(currentAngle));
+
+        if (roundness != 0) {
+             float cp1Theta = (float) (atan2(previousY, previousX) - M_PI / 2.0);
+             float cp1Dx = (float) cos(cp1Theta);
+             float cp1Dy = (float) sin(cp1Theta);
+
+             float cp2Theta = (float) (atan2(y, x) - M_PI / 2.0);
+             float cp2Dx = (float) cos(cp2Theta);
+             float cp2Dy = (float) sin(cp2Theta);
+
+             float cp1x = radius * roundness * POLYGON_MAGIC_NUMBER * cp1Dx;
+             float cp1y = radius * roundness * POLYGON_MAGIC_NUMBER * cp1Dy;
+             float cp2x = radius * roundness * POLYGON_MAGIC_NUMBER * cp2Dx;
+             float cp2y = radius * roundness * POLYGON_MAGIC_NUMBER * cp2Dy;
+
+             cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
+                     VPointF(x + cp2x + cx, y + cp2y + cy),
+                     VPointF(x, y));
+        } else {
+             lineTo(VPointF(x + cx, y + cy));
+        }
+
+        currentAngle += anglePerPoint;
+   }
+
+   close();
+}
+
+void VPath::transform(const VMatrix &m)
+{
+    if (isEmpty()) return;
+    detach();
+    d->transform(m);
+}
diff --git a/src/vector/vpath.h b/src/vector/vpath.h
new file mode 100644 (file)
index 0000000..93c4f53
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef VPATH_H
+#define VPATH_H
+#include "vpoint.h"
+#include "vrect.h"
+#include "vmatrix.h"
+#include<vector>
+
+struct VPathData;
+class VPath
+{
+public:
+    enum class Direction {
+        CCW,
+        CW
+    };
+
+    enum class Element : uchar {
+        MoveTo,
+        LineTo,
+        CubicTo,
+        Close
+    };
+    ~VPath();
+    VPath();
+    VPath(const VPath &path);
+    VPath(VPath &&other);
+    VPath &operator=(const VPath &);
+    VPath &operator=(VPath &&other);
+    bool isEmpty()const;
+    void moveTo(const VPointF &p);
+    inline void moveTo(float x, float y);
+    void lineTo(const VPointF &p);
+    inline void lineTo(float x, float y);
+    void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
+    inline void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey);
+    void arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo);
+    void close();
+    void reset();
+    void reserve(int num_elm);
+
+    void addCircle(float cx, float cy, float radius, VPath::Direction dir = Direction::CW);
+    void addOval(const VRectF &rect, VPath::Direction dir = Direction::CW);
+    void addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir = Direction::CW);
+    void addRect(const VRectF &rect, VPath::Direction dir = Direction::CW);
+    void addPolystarStar(float startAngle, float cx, float cy, float points,
+                         float innerRadius, float outerRadius,
+                         float innerRoundness, float outerRoundness,
+                         VPath::Direction dir = Direction::CW);
+    void addPolystarPolygon(float startAngle, float cx, float cy, float points,
+                            float radius, float roundness,
+                            VPath::Direction dir = Direction::CW);
+
+    void transform(const VMatrix &m);
+    const std::vector<VPath::Element> &elements() const;
+    const std::vector<VPointF> &points() const;
+private:
+    friend class VRaster;
+    int segments() const;
+    VPath copy() const;
+    void detach();
+    void cleanUp(VPathData *x);
+    VPathData *d;
+};
+
+inline void VPath::lineTo(float x, float y)
+{
+    lineTo(VPointF(x,y));
+}
+
+inline void VPath::moveTo(float x, float y)
+{
+    moveTo(VPointF(x,y));
+}
+
+inline void VPath::cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey)
+{
+      cubicTo(VPointF(c1x, c1y), VPointF(c2x, c2y), VPointF(ex, ey));
+}
+
+#endif // VPATH_H
diff --git a/src/vector/vpathmesure.cpp b/src/vector/vpathmesure.cpp
new file mode 100644 (file)
index 0000000..358721c
--- /dev/null
@@ -0,0 +1,6 @@
+#include"vpathmesure.h"
+
+class VPathMesureData
+{
+   VPath      *path;
+};
\ No newline at end of file
diff --git a/src/vector/vpathmesure.h b/src/vector/vpathmesure.h
new file mode 100644 (file)
index 0000000..98605f7
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef VPATHMESURE_H
+#define VPATHMESURE_H
+
+#include "vpath.h"
+
+class VPathMesureData;
+class VPathMesure
+{
+public:
+    ~VPathMesure();
+    VPathMesure();
+    VPathMesure(const VPath *path, bool foceClose);
+    int getLength() const;
+private:
+    VPathMesureData *d;
+};
+
+#endif // VPATHMESURE_H
diff --git a/src/vector/vpoint.h b/src/vector/vpoint.h
new file mode 100644 (file)
index 0000000..7eb2af5
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef VPOINT_H
+#define VPOINT_H
+
+#include"vglobal.h"
+
+class  VPointF
+{
+public:
+    constexpr inline VPointF() noexcept :mx(0), my(0){}
+    constexpr inline VPointF(float x, float y) noexcept :mx(x), my(y){}
+    constexpr inline float x() const noexcept {return mx;}
+    constexpr inline float y() const noexcept {return my;}
+    inline float& rx() noexcept {return mx;}
+    inline float& ry() noexcept {return my;}
+    inline void setX(float x) {mx = x;}
+    inline void setY(float y) {my = y;}
+    inline VPointF operator-() noexcept { return VPointF(-mx, -my); }
+    inline VPointF &operator+=(const VPointF &p) noexcept;
+    inline VPointF &operator-=(const VPointF &p) noexcept;
+    friend const  VPointF operator+(const VPointF & p1, const VPointF & p2) {
+        return VPointF(p1.mx + p2.mx , p1.my + p2.my);
+    }
+    inline friend const bool fuzzyCompare(const VPointF & p1, const VPointF & p2);
+    inline friend VDebug& operator<<(VDebug& os, const VPointF& o);
+
+    friend inline VPointF operator-(const VPointF &p1, const VPointF &p2);
+    friend inline const VPointF operator*(const VPointF &, float val);
+    friend inline const VPointF operator*(float val, const VPointF &);
+    friend inline const VPointF operator/(const VPointF &, float val);
+    friend inline const VPointF operator/(float val, const VPointF &);
+private:
+    float mx;
+    float my;
+};
+
+inline const bool fuzzyCompare(const VPointF & p1, const VPointF & p2)
+{
+    return (floatCmp(p1.mx , p2.mx) && floatCmp(p1.my , p2.my));
+}
+
+
+inline VPointF operator-(const VPointF &p1, const VPointF &p2)
+{
+    return VPointF(p1.mx-p2.mx, p1.my-p2.my);
+}
+
+inline const VPointF operator*(const VPointF &p, float c)
+{ return VPointF(p.mx*c, p.my*c); }
+
+inline const VPointF operator*(float c, const VPointF &p)
+{  return VPointF(p.mx*c, p.my*c); }
+
+inline const VPointF operator/(const VPointF &p, float c)
+{ return VPointF(p.mx/c, p.my/c); }
+
+inline const VPointF operator/(float c, const VPointF &p)
+{  return VPointF(p.mx/c, p.my/c); }
+
+inline VDebug& operator<<(VDebug& os, const VPointF& o)
+{
+    os<<"{P "<<o.x()<<","<<o.y()<<"}";
+    return os;
+}
+
+inline VPointF &VPointF::operator+=(const VPointF &p) noexcept
+{ mx+=p.mx; my+=p.my; return *this; }
+
+inline VPointF &VPointF::operator-=(const VPointF &p) noexcept
+{ mx-=p.mx; my-=p.my; return *this;  }
+
+class  VPoint
+{
+public:
+    constexpr inline VPoint() noexcept :mx(0), my(0){}
+    constexpr inline VPoint(int x, int y) noexcept :mx(x), my(y){}
+    constexpr inline int x() const noexcept {return mx;}
+    constexpr inline int y() const noexcept {return my;}
+    inline void setX(int x) {mx = x;}
+    inline void setY(int y) {my = y;}
+    inline VPoint &operator+=(const VPoint &p) noexcept;
+    inline VPoint &operator-=(const VPoint &p) noexcept;
+    constexpr inline bool operator==(const VPoint &o) const;
+    constexpr inline bool operator!=(const VPoint &o) const { return !(operator==(o)); }
+    friend inline VPoint operator-(const VPoint &p1, const VPoint &p2);
+    inline friend VDebug& operator<<(VDebug& os, const VPoint& o);
+private:
+    int mx;
+    int my;
+};
+inline VDebug& operator<<(VDebug& os, const VPoint& o)
+{
+    os<<"{P "<<o.x()<<","<<o.y()<<"}";
+    return os;
+}
+
+inline VPoint operator-(const VPoint &p1, const VPoint &p2)
+{
+    return VPoint(p1.mx-p2.mx, p1.my-p2.my);
+}
+
+constexpr inline bool VPoint::operator ==(const VPoint &o) const
+{
+    return (mx == o.x() && my == o.y());
+}
+
+inline VPoint &VPoint::operator+=(const VPoint &p) noexcept
+{ mx+=p.mx; my+=p.my; return *this; }
+
+inline VPoint &VPoint::operator-=(const VPoint &p) noexcept
+{ mx-=p.mx; my-=p.my; return *this;  }
+
+
+class  VSize
+{
+public:
+    constexpr inline VSize() noexcept :mw(0), mh(0){}
+    constexpr inline VSize(int w, int h) noexcept :mw(w), mh(h){}
+    constexpr inline int width() const noexcept {return mw;}
+    constexpr inline int height() const noexcept {return mh;}
+    inline void setWidth(int w) {mw = w;}
+    inline void setHeight(int h) {mh = h;}
+    inline VSize &operator+=(const VSize &p) noexcept;
+    inline VSize &operator-=(const VSize &p) noexcept;
+    constexpr inline bool operator==(const VSize &o) const;
+    constexpr inline bool operator!=(const VSize &o) const { return !(operator==(o)); }
+    inline friend VDebug& operator<<(VDebug& os, const VSize& o);
+private:
+    int mw;
+    int mh;
+};
+inline VDebug& operator<<(VDebug& os, const VSize& o)
+{
+    os<<"{P "<<o.width()<<","<<o.height()<<"}";
+    return os;
+}
+constexpr inline bool VSize::operator ==(const VSize &o) const
+{
+    return (mw == o.width() && mh == o.height());
+}
+
+inline VSize &VSize::operator+=(const VSize &p) noexcept
+{ mw+=p.mw; mh+=p.mh; return *this; }
+
+inline VSize &VSize::operator-=(const VSize &p) noexcept
+{ mw-=p.mw; mh-=p.mh; return *this;  }
+
+#endif // VPOINT_H
diff --git a/src/vector/vraster.cpp b/src/vector/vraster.cpp
new file mode 100644 (file)
index 0000000..0bd3a2c
--- /dev/null
@@ -0,0 +1,314 @@
+#include"vraster.h"
+#include"v_ft_raster.h"
+#include"v_ft_stroker.h"
+#include"vpath.h"
+#include"vmatrix.h"
+#include<cstring>
+#include"vdebug.h"
+
+struct FTOutline
+{
+public:
+    FTOutline() = delete;
+    FTOutline(int points, int segments)
+    {
+        ft.points = new SW_FT_Vector[points + segments];
+        ft.tags   = new char[points + segments];
+        ft.contours = new short[segments];
+        ft.n_points = ft.n_contours = 0;
+        ft.flags = 0x0;
+    }
+    void moveTo(const VPointF &pt);
+    void lineTo(const VPointF &pt);
+    void cubicTo(const VPointF &ctr1, const VPointF &ctr2, const VPointF end);
+    void close();
+    void end();
+    void transform(const VMatrix &m);
+    ~FTOutline()
+    {
+        delete[] ft.points;
+        delete[] ft.tags;
+        delete[] ft.contours;
+    }
+    SW_FT_Outline  ft;
+    bool           closed;
+};
+
+#define TO_FT_COORD(x) ((x) * 64) // to freetype 26.6 coordinate.
+
+void FTOutline::transform(const VMatrix &m)
+{
+    VPointF pt;
+    if (m.isIdentity()) return;
+    for (auto i = 0; i < ft.n_points; i++) {
+        pt = m.map(VPointF(ft.points[i].x/64.0, ft.points[i].y/64.0));
+        ft.points[i].x = TO_FT_COORD(pt.x());
+        ft.points[i].y = TO_FT_COORD(pt.y());
+    }
+}
+
+void FTOutline::moveTo(const VPointF &pt)
+{
+    ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
+    ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
+    ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
+    if (ft.n_points) {
+        ft.contours[ft.n_contours] = ft.n_points - 1;
+        ft.n_contours++;
+    }
+    ft.n_points++;
+    closed = false;
+}
+
+void FTOutline::lineTo(const VPointF &pt)
+{
+    ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
+    ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
+    ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
+    ft.n_points++;
+    closed = false;
+}
+
+void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF ep)
+{
+    ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
+    ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
+    ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
+    ft.n_points++;
+
+    ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
+    ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
+    ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
+    ft.n_points++;
+
+    ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
+    ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
+    ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
+    ft.n_points++;
+    closed = false;
+}
+void FTOutline::close()
+{
+    int index;
+    if (ft.n_contours) {
+        index = ft.contours[ft.n_contours - 1] + 1;
+    } else {
+        index = 0;
+    }
+
+    // make sure atleast 1 point exists in the segment.
+    if (ft.n_points == index) {
+        closed = false;
+        return;
+    }
+
+    ft.points[ft.n_points].x = ft.points[index].x;
+    ft.points[ft.n_points].y = ft.points[index].y;
+    ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
+    ft.n_points++;
+    closed = true;
+}
+
+void FTOutline::end()
+{
+    if (ft.n_points) {
+        ft.contours[ft.n_contours] = ft.n_points - 1;
+        ft.n_contours++;
+    }
+}
+
+struct VRasterPrivate
+{
+public:
+    VRle generateFillInfoAsync(const SW_FT_Outline *outline);
+    VRle generateStrokeInfoAsync(const SW_FT_Outline *outline, SW_FT_Stroker_LineCap cap,
+                                 SW_FT_Stroker_LineJoin join,
+                                 int width, int meterLimit,
+                                 SW_FT_Bool closed);
+
+    std::mutex        m_rasterAcess;
+    std::mutex        m_strokerAcess;
+    SW_FT_Raster      m_raster;
+    SW_FT_Stroker     m_stroker;
+};
+
+struct SpanInfo
+{
+  VRle::Span *spans;
+  int          size;
+};
+
+static void
+rleGenerationCb( int count, const SW_FT_Span*  spans,void *user)
+{
+   VRle *rle = (VRle *) user;
+   VRle::Span *rleSpan = (VRle::Span *)spans;
+   rle->addSpan(rleSpan, count);
+}
+
+VRle VRasterPrivate::generateFillInfoAsync(const SW_FT_Outline *outline)
+{
+    m_rasterAcess.lock();
+    VRle rle;
+    SW_FT_Raster_Params params;
+
+    params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ;
+    params.gray_spans = &rleGenerationCb;
+    params.user = &rle;
+    params.source = outline;
+
+    sw_ft_grays_raster.raster_render(m_raster, &params);
+
+    m_rasterAcess.unlock();
+
+    return rle;
+}
+
+VRle VRasterPrivate::generateStrokeInfoAsync(const SW_FT_Outline *outline, SW_FT_Stroker_LineCap cap,
+                                             SW_FT_Stroker_LineJoin join,
+                                             int width, int meterLimit,
+                                             SW_FT_Bool closed)
+{
+    m_strokerAcess.lock();
+    uint points,contors;
+    SW_FT_Outline strokeOutline = { 0, 0, nullptr, nullptr, nullptr, SW_FT_OUTLINE_NONE };
+
+    SW_FT_Stroker_Set(m_stroker, width, cap, join, meterLimit);
+    SW_FT_Stroker_ParseOutline(m_stroker, outline, !closed);
+    SW_FT_Stroker_GetCounts(m_stroker,&points, &contors);
+
+    strokeOutline.points = (SW_FT_Vector *) calloc(points, sizeof(SW_FT_Vector));
+    strokeOutline.tags = (char *) calloc(points, sizeof(char));
+    strokeOutline.contours = (short *) calloc(contors, sizeof(short));
+
+    SW_FT_Stroker_Export(m_stroker, &strokeOutline);
+
+    m_strokerAcess.unlock();
+
+    VRle rle = generateFillInfoAsync(&strokeOutline);
+
+    // cleanup the outline data.
+    free(strokeOutline.points);
+    free(strokeOutline.tags);
+    free(strokeOutline.contours);
+
+    return rle;
+}
+
+
+VRaster::VRaster()
+{
+    d = new VRasterPrivate;
+    sw_ft_grays_raster.raster_new(&d->m_raster);
+    SW_FT_Stroker_New(&d->m_stroker);
+    SW_FT_Stroker_Set(d->m_stroker, 1 << 6,
+                      SW_FT_STROKER_LINECAP_BUTT, SW_FT_STROKER_LINEJOIN_MITER, 0);
+}
+
+VRaster::~VRaster()
+{
+    sw_ft_grays_raster.raster_done(d->m_raster);
+    SW_FT_Stroker_Done(d->m_stroker);
+}
+
+void VRaster::deleteFTOutline(FTOutline *outline)
+{
+    delete outline;
+}
+
+FTOutline *VRaster::toFTOutline(const VPath &path)
+{
+    if (path.isEmpty())
+        return nullptr;
+
+    const std::vector<VPath::Element> &elements = path.elements();
+    const std::vector<VPointF> &points = path.points();
+
+    FTOutline *outline = new FTOutline(points.size(), path.segments());
+
+    int index = 0;
+    for(auto element : elements) {
+        switch (element){
+        case VPath::Element::MoveTo:
+            outline->moveTo(points[index]);
+            index++;
+            break;
+        case VPath::Element::LineTo:
+            outline->lineTo(points[index]);
+            index++;
+            break;
+        case VPath::Element::CubicTo:
+            outline->cubicTo(points[index], points[index+1], points[index+2]);
+            index = index+3;
+            break;
+        case VPath::Element::Close:
+            outline->close();
+            break;
+        default:
+            break;
+        }
+    }
+    outline->end();
+    return outline;
+}
+
+VRle VRaster::generateFillInfo(const FTOutline *outline, FillRule fillRule)
+{
+    int fillRuleFlag = SW_FT_OUTLINE_NONE;
+    switch (fillRule) {
+    case FillRule::EvenOdd:
+        fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
+        break;
+    default:
+        fillRuleFlag = SW_FT_OUTLINE_NONE;
+        break;
+    }
+    FTOutline *outlineRef = const_cast<FTOutline *>(outline);
+    outlineRef->ft.flags =  fillRuleFlag;
+    return d->generateFillInfoAsync(&outlineRef->ft);
+}
+
+VRle VRaster::generateStrokeInfo(const FTOutline *outline, CapStyle cap, JoinStyle join,
+                                 float width, float meterLimit)
+{
+    SW_FT_Stroker_LineCap ftCap;
+    SW_FT_Stroker_LineJoin ftJoin;
+    int ftWidth;
+    int ftMeterLimit;
+    SW_FT_Bool ftbool = (SW_FT_Bool) outline->closed;
+
+    // map strokeWidth to freetype. It uses as the radius of the pen not the diameter
+    width = width/2.0;
+    // convert to freetype co-ordinate
+    ftWidth = int(width * 64);
+    ftMeterLimit = int(meterLimit * 64);
+
+    // map to freetype capstyle
+    switch (cap)
+      {
+         case CapStyle::Square:
+           ftCap = SW_FT_STROKER_LINECAP_SQUARE;
+           break;
+         case CapStyle::Round:
+           ftCap = SW_FT_STROKER_LINECAP_ROUND;
+           break;
+         default:
+           ftCap = SW_FT_STROKER_LINECAP_BUTT;
+           break;
+      }
+    switch (join)
+      {
+         case JoinStyle::Bevel:
+           ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
+           break;
+         case JoinStyle::Round:
+           ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
+           break;
+         default:
+           ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
+           break;
+      }
+
+    return d->generateStrokeInfoAsync(&outline->ft, ftCap, ftJoin,
+                                      ftWidth, ftMeterLimit, ftbool);
+}
diff --git a/src/vector/vraster.h b/src/vector/vraster.h
new file mode 100644 (file)
index 0000000..7beda50
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef VRASTER_H
+#define VRASTER_H
+#include"vrle.h"
+#include<future>
+
+struct FTOutline;
+class VPath;
+
+struct VRasterPrivate;
+class VRaster
+{
+public:
+    ~VRaster();
+    static VRaster &instance()
+    {
+        static VRaster Singleton;
+        return Singleton;
+    }
+    VRaster(const VRaster &other) = delete;
+    VRaster(VRaster&&) = delete;
+    VRaster& operator=(VRaster const&) = delete;
+    VRaster& operator=(VRaster &&) = delete;
+
+    static FTOutline *toFTOutline(const VPath &path);
+    static void deleteFTOutline(FTOutline *);
+    VRle generateFillInfo(const FTOutline *, FillRule fillRule = FillRule::Winding);
+    VRle generateStrokeInfo(const FTOutline *, CapStyle cap, JoinStyle join,
+                            float width, float meterLimit);
+private:
+    VRaster();
+    VRasterPrivate *d;
+};
+#endif // VRASTER_H
diff --git a/src/vector/vrect.cpp b/src/vector/vrect.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/vector/vrect.h b/src/vector/vrect.h
new file mode 100644 (file)
index 0000000..0f002df
--- /dev/null
@@ -0,0 +1,184 @@
+#ifndef VRECT_H
+#define VRECT_H
+#include"vglobal.h"
+#include"vpoint.h"
+
+class  VRect
+{
+public:
+    V_CONSTEXPR VRect(): x1(0), y1(0), x2(-1), y2(-1) {}
+    V_CONSTEXPR VRect(int left, int top, int width, int height);
+    V_CONSTEXPR inline bool isEmpty() const;
+    V_CONSTEXPR inline bool isNull() const;
+
+    V_CONSTEXPR inline int left() const ;
+    V_CONSTEXPR inline int top() const ;
+    V_CONSTEXPR inline int right() const ;
+    V_CONSTEXPR inline int bottom() const ;
+    V_CONSTEXPR inline int width() const ;
+    V_CONSTEXPR inline int height() const ;
+    V_CONSTEXPR inline int x() const ;
+    V_CONSTEXPR inline int y() const ;
+    inline void setLeft(int l) {x1 = l;}
+    inline void setTop(int t) {y1 = t;}
+    inline void setRight(int r) {x2 = r;}
+    inline void setBottom(int b) {y2 = b;}
+    inline void setWidth(int w) {x2 = x1 + w;}
+    inline void setHeight(int h) {y2 = y1 + h;}
+    inline VRect translated(int dx, int dy) const ;
+    inline void translate(int dx, int dy);
+    inline bool contains(const VRect &r, bool proper = false) const;
+    inline bool intersects(const VRect &r);
+    friend V_CONSTEXPR inline bool operator==(const VRect &, const VRect &) noexcept;
+    friend V_CONSTEXPR inline bool operator!=(const VRect &, const VRect &) noexcept;
+    friend VDebug& operator<<(VDebug& os, const VRect& o);
+private:
+    int x1;
+    int y1;
+    int x2;
+    int y2;
+};
+
+inline bool VRect::intersects(const VRect &r)
+{
+    return (right() > r.left() && left() < r.right() &&
+            bottom() > r.top() && top() < r.bottom());
+}
+
+inline VDebug& operator<<(VDebug& os, const VRect& o)
+{
+    os<<"{R "<<o.x()<<","<<o.y()<<","<<o.width()<<","<<o.height()<<"}";
+    return os;
+}
+V_CONSTEXPR inline bool operator==(const VRect &r1, const VRect &r2) noexcept
+{
+    return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2;
+}
+
+V_CONSTEXPR inline bool operator!=(const VRect &r1, const VRect &r2) noexcept
+{
+    return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2;
+}
+
+V_CONSTEXPR inline bool VRect::isEmpty() const
+{ return x1 > x2 || y1 > y2; }
+
+V_CONSTEXPR inline bool VRect::isNull() const
+{ return (((x2-x1) == 0) || ((y2 - y1) == 0)); }
+
+V_CONSTEXPR inline int VRect::x() const
+{ return x1; }
+
+V_CONSTEXPR inline int VRect::y() const
+{ return y1; }
+
+V_CONSTEXPR inline int VRect::left() const
+{ return x1; }
+
+V_CONSTEXPR inline int VRect::top() const
+{ return y1; }
+
+V_CONSTEXPR inline int VRect::right() const
+{ return x2; }
+
+V_CONSTEXPR inline int VRect::bottom() const
+{ return y2; }
+V_CONSTEXPR inline int VRect::width() const
+{ return x2 - x1; }
+V_CONSTEXPR inline int VRect::height() const
+{ return y2 - y1; }
+
+inline VRect VRect::translated(int dx, int dy) const
+{ return VRect(x1+dx, y1+dy, x2-x1, y2-y1); }
+
+inline void VRect::translate(int dx, int dy)
+{
+    x1 += dx;
+    y1 += dy;
+    x2 += dx;
+    y2 += dy;
+}
+inline bool VRect::contains(const VRect &r, bool proper) const
+{
+    if (!proper) {
+        if ((x1 <= r.x1) &&
+            (x2 >= r.x2) &&
+            (y1 <= r.y1) &&
+            (y2 >= r.y2))
+            return true;
+        return false;
+    }else {
+        if ((x1 < r.x1) &&
+            (x2 > r.x2) &&
+            (y1 < r.y1) &&
+            (y2 > r.y2))
+            return true;
+        return false;
+    }
+
+}
+V_CONSTEXPR inline VRect::VRect(int left, int top, int width, int height):
+    x1(left), y1(top), x2(width + left), y2(height + top){}
+
+class  VRectF
+{
+public:
+    V_CONSTEXPR VRectF(): x1(0), y1(0), x2(-1), y2(-1) {}
+    VRectF(float left, float top, float width, float height){
+        x1 = left; y1 = top; x2 = x1 + width; y2 = y1 + height;
+    }
+
+    V_CONSTEXPR inline bool isEmpty() const;
+    V_CONSTEXPR inline bool isNull() const;
+    V_CONSTEXPR inline float left() const ;
+    V_CONSTEXPR inline float top() const ;
+    V_CONSTEXPR inline float right() const ;
+    V_CONSTEXPR inline float bottom() const ;
+    V_CONSTEXPR inline float width() const ;
+    V_CONSTEXPR inline float height() const ;
+    V_CONSTEXPR inline float x() const ;
+    V_CONSTEXPR inline float y() const ;
+    V_CONSTEXPR inline VPointF center() const {return VPointF(x1 + (x2-x1)/2.f , y1 + (y2-y1)/2.f);}
+    inline void setLeft(float l) {x1 = l;}
+    inline void setTop(float t) {y1 = t;}
+    inline void setRight(float r) {x2 = r;}
+    inline void setBottom(float b) {y2 = b;}
+    inline void setWidth(float w) {x2 = x1 + w;}
+    inline void setHeight(float h) {y2 = y1 + h;}
+    inline void translate(float dx, float dy) {x1 -= dx; x2 -= dx; y1 -= dx; y2 -= dx;}
+private:
+    float x1;
+    float y1;
+    float x2;
+    float y2;
+};
+
+V_CONSTEXPR inline bool VRectF::isEmpty() const
+{ return x1 > x2 || y1 > y2; }
+
+V_CONSTEXPR inline bool VRectF::isNull() const
+{ return (((x2-x1) == 0) || ((y2 - y1) == 0)); }
+
+V_CONSTEXPR inline float VRectF::x() const
+{ return x1; }
+
+V_CONSTEXPR inline float VRectF::y() const
+{ return y1; }
+
+V_CONSTEXPR inline float VRectF::left() const
+{ return x1; }
+
+V_CONSTEXPR inline float VRectF::top() const
+{ return y1; }
+
+V_CONSTEXPR inline float VRectF::right() const
+{ return x2; }
+
+V_CONSTEXPR inline float VRectF::bottom() const
+{ return y2; }
+V_CONSTEXPR inline float VRectF::width() const
+{ return x2 - x1; }
+V_CONSTEXPR inline float VRectF::height() const
+{ return y2 - y1; }
+
+#endif // VRECT_H
diff --git a/src/vector/vregion.cpp b/src/vector/vregion.cpp
new file mode 100644 (file)
index 0000000..3db9ad2
--- /dev/null
@@ -0,0 +1,2368 @@
+/*
+ * Copyright 1987, 1988, 1989, 1998  The Open Group
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.
+ *
+ * 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
+ * OPEN GROUP 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.
+ *
+ * Except as contained in this notice, the name of The Open Group shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from The Open Group.
+ *
+ * Copyright 1987, 1988, 1989 by
+ * Digital Equipment Corporation, Maynard, Massachusetts.
+ *
+ *                    All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Copyright Â© 1998 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  Keith Packard makes no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cstdint>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+
+#define critical_if_fail assert
+#define PIXMAN_EXPORT static
+#define FALSE 0
+#define TRUE 1
+#define FUNC ""
+#define MIN(a, b) (a) < (b) ? (a) : (b)
+#define MAX(a, b) (a) > (b) ? (a) : (b)
+
+typedef int pixman_bool_t;
+
+typedef struct pixman_rectangle  pixman_rectangle_t;
+
+typedef struct pixman_box          box_type_t;
+typedef struct pixman_region_data    region_data_type_t;
+typedef struct pixman_region       region_type_t;
+typedef int64_t                      overflow_int_t;
+
+#define PREFIX(x) pixman_region##x
+
+#define PIXMAN_REGION_MAX INT32_MAX
+#define PIXMAN_REGION_MIN INT32_MIN
+
+
+typedef struct {
+    int x, y;
+} point_type_t;
+
+struct pixman_region_data {
+    long    size;
+    long    numRects;
+/*  box_type_t rects[size];   in memory but not explicitly declared */
+};
+
+struct pixman_rectangle
+{
+    int32_t x, y;
+    uint32_t width, height;
+};
+
+struct pixman_box
+{
+    int32_t x1, y1, x2, y2;
+};
+
+struct pixman_region
+{
+    box_type_t          extents;
+    region_data_type_t  *data;
+};
+
+typedef enum
+{
+    PIXMAN_REGION_OUT,
+    PIXMAN_REGION_IN,
+    PIXMAN_REGION_PART
+} pixman_region_overlap_t;
+
+static void
+_pixman_log_error (const char *function, const char *message)
+{
+    fprintf (stderr,
+         "*** BUG ***\n"
+         "In %s: %s\n"
+         "Set a breakpoint on '_pixman_log_error' to debug\n\n",
+                 function, message);
+}
+
+
+
+#define PIXREGION_NIL(reg) ((reg)->data && !(reg)->data->numRects)
+/* not a region */
+#define PIXREGION_NAR(reg)      ((reg)->data == pixman_broken_data)
+#define PIXREGION_NUMRECTS(reg) ((reg)->data ? (reg)->data->numRects : 1)
+#define PIXREGION_SIZE(reg) ((reg)->data ? (reg)->data->size : 0)
+#define PIXREGION_RECTS(reg) \
+    ((reg)->data ? (box_type_t *)((reg)->data + 1) \
+     : &(reg)->extents)
+#define PIXREGION_BOXPTR(reg) ((box_type_t *)((reg)->data + 1))
+#define PIXREGION_BOX(reg, i) (&PIXREGION_BOXPTR (reg)[i])
+#define PIXREGION_TOP(reg) PIXREGION_BOX (reg, (reg)->data->numRects)
+#define PIXREGION_END(reg) PIXREGION_BOX (reg, (reg)->data->numRects - 1)
+
+#define GOOD_RECT(rect) ((rect)->x1 < (rect)->x2 && (rect)->y1 < (rect)->y2)
+#define BAD_RECT(rect) ((rect)->x1 > (rect)->x2 || (rect)->y1 > (rect)->y2)
+
+#ifdef DEBUG
+
+#define GOOD(reg)                   \
+    do                           \
+    {                         \
+    if (!PREFIX (_selfcheck (reg)))             \
+        _pixman_log_error (FUNC, "Malformed region " # reg);   \
+    } while (0)
+
+#else
+
+#define GOOD(reg)
+
+#endif
+
+static const box_type_t PREFIX (_empty_box_) = { 0, 0, 0, 0 };
+static const region_data_type_t PREFIX (_empty_data_) = { 0, 0 };
+#if defined (__llvm__) && !defined (__clang__)
+static const volatile region_data_type_t PREFIX (_broken_data_) = { 0, 0 };
+#else
+static const region_data_type_t PREFIX (_broken_data_) = { 0, 0 };
+#endif
+
+static box_type_t *pixman_region_empty_box =
+    (box_type_t *)&PREFIX (_empty_box_);
+static region_data_type_t *pixman_region_empty_data =
+    (region_data_type_t *)&PREFIX (_empty_data_);
+static region_data_type_t *pixman_broken_data =
+    (region_data_type_t *)&PREFIX (_broken_data_);
+
+static pixman_bool_t
+pixman_break (region_type_t *region);
+
+/*
+ * The functions in this file implement the Region abstraction used extensively
+ * throughout the X11 sample server. A Region is simply a set of disjoint
+ * (non-overlapping) rectangles, plus an "extent" rectangle which is the
+ * smallest single rectangle that contains all the non-overlapping rectangles.
+ *
+ * A Region is implemented as a "y-x-banded" array of rectangles.  This array
+ * imposes two degrees of order.  First, all rectangles are sorted by top side
+ * y coordinate first (y1), and then by left side x coordinate (x1).
+ *
+ * Furthermore, the rectangles are grouped into "bands".  Each rectangle in a
+ * band has the same top y coordinate (y1), and each has the same bottom y
+ * coordinate (y2).  Thus all rectangles in a band differ only in their left
+ * and right side (x1 and x2).  Bands are implicit in the array of rectangles:
+ * there is no separate list of band start pointers.
+ *
+ * The y-x band representation does not minimize rectangles.  In particular,
+ * if a rectangle vertically crosses a band (the rectangle has scanlines in
+ * the y1 to y2 area spanned by the band), then the rectangle may be broken
+ * down into two or more smaller rectangles stacked one atop the other.
+ *
+ *  -----------                -----------
+ *  |         |                |         |          band 0
+ *  |         |  --------         -----------  --------
+ *  |         |  |      |  in y-x banded    |         |  |      |   band 1
+ *  |         |  |      |  form is      |         |  |      |
+ *  -----------  |      |         -----------  --------
+ *               |      |            |      |   band 2
+ *               --------            --------
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible: no two rectangles within a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course).
+ *
+ * Adam de Boor wrote most of the original region code.  Joel McCormack
+ * substantially modified or rewrote most of the core arithmetic routines, and
+ * added pixman_region_validate in order to support several speed improvements
+ * to pixman_region_validate_tree.  Bob Scheifler changed the representation
+ * to be more compact when empty or a single rectangle, and did a bunch of
+ * gratuitous reformatting. Carl Worth did further gratuitous reformatting
+ * while re-merging the server and client region code into libpixregion.
+ * Soren Sandmann did even more gratuitous reformatting.
+ */
+
+/*  true iff two Boxes overlap */
+#define EXTENTCHECK(r1, r2)      \
+    (!( ((r1)->x2 <= (r2)->x1)  || \
+        ((r1)->x1 >= (r2)->x2)  || \
+        ((r1)->y2 <= (r2)->y1)  || \
+        ((r1)->y1 >= (r2)->y2) ) )
+
+/* true iff (x,y) is in Box */
+#define INBOX(r, x, y)  \
+    ( ((r)->x2 >  x) && \
+      ((r)->x1 <= x) && \
+      ((r)->y2 >  y) && \
+      ((r)->y1 <= y) )
+
+/* true iff Box r1 contains Box r2 */
+#define SUBSUMES(r1, r2)   \
+    ( ((r1)->x1 <= (r2)->x1) && \
+      ((r1)->x2 >= (r2)->x2) && \
+      ((r1)->y1 <= (r2)->y1) && \
+      ((r1)->y2 >= (r2)->y2) )
+
+static size_t
+PIXREGION_SZOF (size_t n)
+{
+    size_t size = n * sizeof(box_type_t);
+
+    if (n > UINT32_MAX / sizeof(box_type_t))
+    return 0;
+
+    if (sizeof(region_data_type_t) > UINT32_MAX - size)
+    return 0;
+
+    return size + sizeof(region_data_type_t);
+}
+
+static region_data_type_t *
+alloc_data (size_t n)
+{
+    size_t sz = PIXREGION_SZOF (n);
+
+    if (!sz)
+    return NULL;
+
+    return (region_data_type_t *)malloc (sz);
+}
+
+#define FREE_DATA(reg) if ((reg)->data && (reg)->data->size) free ((reg)->data)
+
+#define RECTALLOC_BAIL(region, n, bail)               \
+    do                           \
+    {                         \
+    if (!(region)->data ||                \
+        (((region)->data->numRects + (n)) > (region)->data->size))   \
+    {                      \
+        if (!pixman_rect_alloc (region, n))           \
+        goto bail;                  \
+    }                      \
+    } while (0)
+
+#define RECTALLOC(region, n)                 \
+    do                           \
+    {                         \
+    if (!(region)->data ||                \
+        (((region)->data->numRects + (n)) > (region)->data->size))   \
+    {                      \
+        if (!pixman_rect_alloc (region, n)) {         \
+        return FALSE;                  \
+        }                        \
+    }                      \
+    } while (0)
+
+#define ADDRECT(next_rect, nx1, ny1, nx2, ny2)      \
+    do                      \
+    {                    \
+    next_rect->x1 = nx1;                        \
+    next_rect->y1 = ny1;                        \
+    next_rect->x2 = nx2;                        \
+    next_rect->y2 = ny2;                        \
+    next_rect++;                                \
+    }                    \
+    while (0)
+
+#define NEWRECT(region, next_rect, nx1, ny1, nx2, ny2)         \
+    do                           \
+    {                         \
+    if (!(region)->data ||                \
+        ((region)->data->numRects == (region)->data->size))    \
+    {                      \
+        if (!pixman_rect_alloc (region, 1))           \
+        return FALSE;                  \
+        next_rect = PIXREGION_TOP (region);           \
+    }                      \
+    ADDRECT (next_rect, nx1, ny1, nx2, ny2);       \
+    region->data->numRects++;             \
+    critical_if_fail (region->data->numRects <= region->data->size);    \
+    } while (0)
+
+#define DOWNSIZE(reg, numRects)                 \
+    do                           \
+    {                         \
+    if (((numRects) < ((reg)->data->size >> 1)) &&       \
+        ((reg)->data->size > 50))               \
+    {                      \
+        region_data_type_t * new_data;          \
+        size_t data_size = PIXREGION_SZOF (numRects);    \
+                                    \
+        if (!data_size)                \
+        {                        \
+        new_data = NULL;               \
+        }                        \
+        else                     \
+        {                        \
+        new_data = (region_data_type_t *)       \
+            realloc ((reg)->data, data_size);         \
+        }                        \
+                                    \
+        if (new_data)                  \
+        {                        \
+        new_data->size = (numRects);            \
+        (reg)->data = new_data;              \
+        }                        \
+    }                      \
+    } while (0)
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_equal) (region_type_t *reg1, region_type_t *reg2)
+{
+    int i;
+    box_type_t *rects1;
+    box_type_t *rects2;
+
+    if (reg1->extents.x1 != reg2->extents.x1)
+    return FALSE;
+
+    if (reg1->extents.x2 != reg2->extents.x2)
+    return FALSE;
+
+    if (reg1->extents.y1 != reg2->extents.y1)
+    return FALSE;
+
+    if (reg1->extents.y2 != reg2->extents.y2)
+    return FALSE;
+
+    if (PIXREGION_NUMRECTS (reg1) != PIXREGION_NUMRECTS (reg2))
+    return FALSE;
+
+    rects1 = PIXREGION_RECTS (reg1);
+    rects2 = PIXREGION_RECTS (reg2);
+
+    for (i = 0; i != PIXREGION_NUMRECTS (reg1); i++)
+    {
+    if (rects1[i].x1 != rects2[i].x1)
+        return FALSE;
+
+    if (rects1[i].x2 != rects2[i].x2)
+        return FALSE;
+
+    if (rects1[i].y1 != rects2[i].y1)
+        return FALSE;
+
+    if (rects1[i].y2 != rects2[i].y2)
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+// returns true if both region intersects
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_intersects) (region_type_t *reg1, region_type_t *reg2)
+{
+    box_type_t *rects1 = PIXREGION_RECTS (reg1);
+    box_type_t *rects2 = PIXREGION_RECTS (reg2);
+    for (int i = 0; i != PIXREGION_NUMRECTS (reg1); i++)
+    {
+        for(int j = 0; j != PIXREGION_NUMRECTS (reg2); j++)
+        {
+            if (EXTENTCHECK(rects1 + i, rects2 + j))
+                return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+int
+PREFIX (_print) (region_type_t *rgn)
+{
+    int num, size;
+    int i;
+    box_type_t * rects;
+
+    num = PIXREGION_NUMRECTS (rgn);
+    size = PIXREGION_SIZE (rgn);
+    rects = PIXREGION_RECTS (rgn);
+
+    fprintf (stderr, "num: %d size: %d\n", num, size);
+    fprintf (stderr, "extents: %d %d %d %d\n",
+             rgn->extents.x1,
+         rgn->extents.y1,
+         rgn->extents.x2,
+         rgn->extents.y2);
+
+    for (i = 0; i < num; i++)
+    {
+    fprintf (stderr, "%d %d %d %d \n",
+             rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
+    }
+
+    fprintf (stderr, "\n");
+
+    return(num);
+}
+
+
+PIXMAN_EXPORT void
+PREFIX (_init) (region_type_t *region)
+{
+    region->extents = *pixman_region_empty_box;
+    region->data = pixman_region_empty_data;
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_union_rect) (region_type_t *dest,
+                      region_type_t *source,
+                      int            x,
+                      int            y,
+                      unsigned int   width,
+                      unsigned int   height);
+PIXMAN_EXPORT void
+PREFIX (_init_rect) (region_type_t *   region,
+                     int      x,
+                     int     y,
+                     unsigned int  width,
+                     unsigned int  height)
+{
+    PREFIX (_init) (region);
+    PREFIX (_union_rect)(region, region, x, y, width, height);
+}
+
+PIXMAN_EXPORT void
+PREFIX (_fini) (region_type_t *region)
+{
+    GOOD (region);
+    FREE_DATA (region);
+}
+
+PIXMAN_EXPORT int
+PREFIX (_n_rects) (region_type_t *region)
+{
+    return PIXREGION_NUMRECTS (region);
+}
+
+static pixman_bool_t
+pixman_break (region_type_t *region)
+{
+    FREE_DATA (region);
+
+    region->extents = *pixman_region_empty_box;
+    region->data = pixman_broken_data;
+
+    return FALSE;
+}
+
+static pixman_bool_t
+pixman_rect_alloc (region_type_t * region,
+                   int             n)
+{
+    region_data_type_t *data;
+
+    if (!region->data)
+    {
+    n++;
+    region->data = alloc_data (n);
+
+    if (!region->data)
+        return pixman_break (region);
+
+    region->data->numRects = 1;
+    *PIXREGION_BOXPTR (region) = region->extents;
+    }
+    else if (!region->data->size)
+    {
+    region->data = alloc_data (n);
+
+    if (!region->data)
+        return pixman_break (region);
+
+    region->data->numRects = 0;
+    }
+    else
+    {
+    size_t data_size;
+
+    if (n == 1)
+    {
+        n = region->data->numRects;
+        if (n > 500) /* XXX pick numbers out of a hat */
+        n = 250;
+    }
+
+    n += region->data->numRects;
+    data_size = PIXREGION_SZOF (n);
+
+    if (!data_size)
+    {
+        data = NULL;
+    }
+    else
+    {
+        data = (region_data_type_t *)
+        realloc (region->data, PIXREGION_SZOF (n));
+    }
+
+    if (!data)
+        return pixman_break (region);
+
+    region->data = data;
+    }
+
+    region->data->size = n;
+
+    return TRUE;
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_copy) (region_type_t *dst, region_type_t *src)
+{
+    GOOD (dst);
+    GOOD (src);
+
+    if (dst == src)
+    return TRUE;
+
+    dst->extents = src->extents;
+
+    if (!src->data || !src->data->size)
+    {
+    FREE_DATA (dst);
+    dst->data = src->data;
+    return TRUE;
+    }
+
+    if (!dst->data || (dst->data->size < src->data->numRects))
+    {
+    FREE_DATA (dst);
+
+    dst->data = alloc_data (src->data->numRects);
+
+    if (!dst->data)
+        return pixman_break (dst);
+
+    dst->data->size = src->data->numRects;
+    }
+
+    dst->data->numRects = src->data->numRects;
+
+    memmove ((char *)PIXREGION_BOXPTR (dst), (char *)PIXREGION_BOXPTR (src),
+             dst->data->numRects * sizeof(box_type_t));
+
+    return TRUE;
+}
+
+/*======================================================================
+ *     Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_coalesce --
+ * Attempt to merge the boxes in the current band with those in the
+ * previous one.  We are guaranteed that the current band extends to
+ *      the end of the rects array.  Used only by pixman_op.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * Side Effects:
+ * If coalescing takes place:
+ *     - rectangles in the previous band will have their y2 fields
+ *       altered.
+ *     - region->data->numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+static inline int
+pixman_coalesce (region_type_t * region,      /* Region to coalesce      */
+         int             prev_start,  /* Index of start of previous band */
+         int             cur_start)   /* Index of start of current band  */
+{
+    box_type_t *prev_box;       /* Current box in previous band        */
+    box_type_t *cur_box;        /* Current box in current band       */
+    int numRects;               /* Number rectangles in both bands   */
+    int y2;                     /* Bottom of current band        */
+
+    /*
+     * Figure out how many rectangles are in the band.
+     */
+    numRects = cur_start - prev_start;
+    critical_if_fail (numRects == region->data->numRects - cur_start);
+
+    if (!numRects) return cur_start;
+
+    /*
+     * The bands may only be coalesced if the bottom of the previous
+     * matches the top scanline of the current.
+     */
+    prev_box = PIXREGION_BOX (region, prev_start);
+    cur_box = PIXREGION_BOX (region, cur_start);
+    if (prev_box->y2 != cur_box->y1) return cur_start;
+
+    /*
+     * Make sure the bands have boxes in the same places. This
+     * assumes that boxes have been added in such a way that they
+     * cover the most area possible. I.e. two boxes in a band must
+     * have some horizontal space between them.
+     */
+    y2 = cur_box->y2;
+
+    do
+    {
+    if ((prev_box->x1 != cur_box->x1) || (prev_box->x2 != cur_box->x2))
+        return (cur_start);
+
+    prev_box++;
+    cur_box++;
+    numRects--;
+    }
+    while (numRects);
+
+    /*
+     * The bands may be merged, so set the bottom y of each box
+     * in the previous band to the bottom y of the current band.
+     */
+    numRects = cur_start - prev_start;
+    region->data->numRects -= numRects;
+
+    do
+    {
+    prev_box--;
+    prev_box->y2 = y2;
+    numRects--;
+    }
+    while (numRects);
+
+    return prev_start;
+}
+
+/* Quicky macro to avoid trivial reject procedure calls to pixman_coalesce */
+
+#define COALESCE(new_reg, prev_band, cur_band)                          \
+    do                           \
+    {                         \
+    if (cur_band - prev_band == new_reg->data->numRects - cur_band)  \
+        prev_band = pixman_coalesce (new_reg, prev_band, cur_band);  \
+    else                      \
+        prev_band = cur_band;             \
+    } while (0)
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_region_append_non_o --
+ * Handle a non-overlapping band for the union and subtract operations.
+ *      Just adds the (top/bottom-clipped) rectangles into the region.
+ *      Doesn't have to check for subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * region->data->numRects is incremented and the rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static inline pixman_bool_t
+pixman_region_append_non_o (region_type_t * region,
+                box_type_t *    r,
+                box_type_t *    r_end,
+                int             y1,
+                int             y2)
+{
+    box_type_t *next_rect;
+    int new_rects;
+
+    new_rects = r_end - r;
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (new_rects != 0);
+
+    /* Make sure we have enough space for all rectangles to be added */
+    RECTALLOC (region, new_rects);
+    next_rect = PIXREGION_TOP (region);
+    region->data->numRects += new_rects;
+
+    do
+    {
+    critical_if_fail (r->x1 < r->x2);
+    ADDRECT (next_rect, r->x1, y1, r->x2, y2);
+    r++;
+    }
+    while (r != r_end);
+
+    return TRUE;
+}
+
+#define FIND_BAND(r, r_band_end, r_end, ry1)            \
+    do                             \
+    {                           \
+    ry1 = r->y1;                      \
+    r_band_end = r + 1;                  \
+    while ((r_band_end != r_end) && (r_band_end->y1 == ry1)) {   \
+        r_band_end++;                    \
+    }                        \
+    } while (0)
+
+#define APPEND_REGIONS(new_reg, r, r_end)          \
+    do                           \
+    {                         \
+    int new_rects;                     \
+    if ((new_rects = r_end - r)) {              \
+        RECTALLOC_BAIL (new_reg, new_rects, bail);       \
+        memmove ((char *)PIXREGION_TOP (new_reg), (char *)r,   \
+             new_rects * sizeof(box_type_t));         \
+        new_reg->data->numRects += new_rects;         \
+    }                      \
+    } while (0)
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_op --
+ * Apply an operation to two regions. Called by pixman_region_union, pixman_region_inverse,
+ * pixman_region_subtract, pixman_region_intersect....  Both regions MUST have at least one
+ *      rectangle, and cannot be the same object.
+ *
+ * Results:
+ * TRUE if successful.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ * overlap set to TRUE if overlap_func ever returns TRUE.
+ *
+ * Notes:
+ * The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the non_overlap_func is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlap_func is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+typedef pixman_bool_t (*overlap_proc_ptr) (region_type_t *region,
+                       box_type_t *   r1,
+                       box_type_t *   r1_end,
+                       box_type_t *   r2,
+                       box_type_t *   r2_end,
+                       int            y1,
+                       int            y2);
+
+static pixman_bool_t
+pixman_op (region_type_t *  new_reg,               /* Place to store result       */
+       region_type_t *  reg1,                  /* First region in operation     */
+       region_type_t *  reg2,                  /* 2d region in operation        */
+       overlap_proc_ptr overlap_func,          /* Function to call for over-
+                            * lapping bands         */
+       int              append_non1,           /* Append non-overlapping bands
+                            * in region 1 ?
+                            */
+       int              append_non2            /* Append non-overlapping bands
+                            * in region 2 ?
+                            */
+    )
+{
+    box_type_t *r1;                 /* Pointer into first region     */
+    box_type_t *r2;                 /* Pointer into 2d region       */
+    box_type_t *r1_end;             /* End of 1st region      */
+    box_type_t *r2_end;             /* End of 2d region          */
+    int ybot;                       /* Bottom of intersection       */
+    int ytop;                       /* Top of intersection       */
+    region_data_type_t *old_data;   /* Old data for new_reg      */
+    int prev_band;                  /* Index of start of
+                     * previous band in new_reg       */
+    int cur_band;                   /* Index of start of current
+                     * band in new_reg         */
+    box_type_t * r1_band_end;       /* End of current band in r1     */
+    box_type_t * r2_band_end;       /* End of current band in r2     */
+    int top;                        /* Top of non-overlapping band   */
+    int bot;                        /* Bottom of non-overlapping band*/
+    int r1y1;                       /* Temps for r1->y1 and r2->y1   */
+    int r2y1;
+    int new_size;
+    int numRects;
+
+    /*
+     * Break any region computed from a broken region
+     */
+    if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2))
+    return pixman_break (new_reg);
+
+    /*
+     * Initialization:
+     *   set r1, r2, r1_end and r2_end appropriately, save the rectangles
+     * of the destination region until the end in case it's one of
+     * the two source regions, then mark the "new" region empty, allocating
+     * another array of rectangles for it to use.
+     */
+
+    r1 = PIXREGION_RECTS (reg1);
+    new_size = PIXREGION_NUMRECTS (reg1);
+    r1_end = r1 + new_size;
+
+    numRects = PIXREGION_NUMRECTS (reg2);
+    r2 = PIXREGION_RECTS (reg2);
+    r2_end = r2 + numRects;
+
+    critical_if_fail (r1 != r1_end);
+    critical_if_fail (r2 != r2_end);
+
+    old_data = (region_data_type_t *)NULL;
+
+    if (((new_reg == reg1) && (new_size > 1)) ||
+        ((new_reg == reg2) && (numRects > 1)))
+    {
+        old_data = new_reg->data;
+        new_reg->data = pixman_region_empty_data;
+    }
+
+    /* guess at new size */
+    if (numRects > new_size)
+    new_size = numRects;
+
+    new_size <<= 1;
+
+    if (!new_reg->data)
+    new_reg->data = pixman_region_empty_data;
+    else if (new_reg->data->size)
+    new_reg->data->numRects = 0;
+
+    if (new_size > new_reg->data->size)
+    {
+        if (!pixman_rect_alloc (new_reg, new_size))
+        {
+            free (old_data);
+            return FALSE;
+    }
+    }
+
+    /*
+     * Initialize ybot.
+     * In the upcoming loop, ybot and ytop serve different functions depending
+     * on whether the band being handled is an overlapping or non-overlapping
+     * band.
+     *  In the case of a non-overlapping band (only one of the regions
+     * has points in the band), ybot is the bottom of the most recent
+     * intersection and thus clips the top of the rectangles in that band.
+     * ytop is the top of the next intersection between the two regions and
+     * serves to clip the bottom of the rectangles in the current band.
+     *   For an overlapping band (where the two regions intersect), ytop clips
+     * the top of the rectangles of both regions and ybot clips the bottoms.
+     */
+
+    ybot = MIN (r1->y1, r2->y1);
+
+    /*
+     * prev_band serves to mark the start of the previous band so rectangles
+     * can be coalesced into larger rectangles. qv. pixman_coalesce, above.
+     * In the beginning, there is no previous band, so prev_band == cur_band
+     * (cur_band is set later on, of course, but the first band will always
+     * start at index 0). prev_band and cur_band must be indices because of
+     * the possible expansion, and resultant moving, of the new region's
+     * array of rectangles.
+     */
+    prev_band = 0;
+
+    do
+    {
+        /*
+     * This algorithm proceeds one source-band (as opposed to a
+     * destination band, which is determined by where the two regions
+     * intersect) at a time. r1_band_end and r2_band_end serve to mark the
+     * rectangle after the last one in the current band for their
+     * respective regions.
+     */
+        critical_if_fail (r1 != r1_end);
+        critical_if_fail (r2 != r2_end);
+
+        FIND_BAND (r1, r1_band_end, r1_end, r1y1);
+        FIND_BAND (r2, r2_band_end, r2_end, r2y1);
+
+        /*
+     * First handle the band that doesn't intersect, if any.
+     *
+     * Note that attention is restricted to one band in the
+     * non-intersecting region at once, so if a region has n
+     * bands between the current position and the next place it overlaps
+     * the other, this entire loop will be passed through n times.
+     */
+        if (r1y1 < r2y1)
+        {
+            if (append_non1)
+            {
+                top = MAX (r1y1, ybot);
+                bot = MIN (r1->y2, r2y1);
+                if (top != bot)
+                {
+                    cur_band = new_reg->data->numRects;
+                    if (!pixman_region_append_non_o (new_reg, r1, r1_band_end, top, bot))
+            goto bail;
+                    COALESCE (new_reg, prev_band, cur_band);
+        }
+        }
+            ytop = r2y1;
+    }
+        else if (r2y1 < r1y1)
+        {
+            if (append_non2)
+            {
+                top = MAX (r2y1, ybot);
+                bot = MIN (r2->y2, r1y1);
+
+                if (top != bot)
+                {
+                    cur_band = new_reg->data->numRects;
+
+                    if (!pixman_region_append_non_o (new_reg, r2, r2_band_end, top, bot))
+            goto bail;
+
+                    COALESCE (new_reg, prev_band, cur_band);
+        }
+        }
+            ytop = r1y1;
+    }
+        else
+        {
+            ytop = r1y1;
+    }
+
+        /*
+     * Now see if we've hit an intersecting band. The two bands only
+     * intersect if ybot > ytop
+     */
+        ybot = MIN (r1->y2, r2->y2);
+        if (ybot > ytop)
+        {
+            cur_band = new_reg->data->numRects;
+
+            if (!(*overlap_func)(new_reg,
+                                 r1, r1_band_end,
+                                 r2, r2_band_end,
+                                 ytop, ybot))
+        {
+        goto bail;
+        }
+
+            COALESCE (new_reg, prev_band, cur_band);
+    }
+
+        /*
+     * If we've finished with a band (y2 == ybot) we skip forward
+     * in the region to the next band.
+     */
+        if (r1->y2 == ybot)
+        r1 = r1_band_end;
+
+        if (r2->y2 == ybot)
+        r2 = r2_band_end;
+
+    }
+    while (r1 != r1_end && r2 != r2_end);
+
+    /*
+     * Deal with whichever region (if any) still has rectangles left.
+     *
+     * We only need to worry about banding and coalescing for the very first
+     * band left.  After that, we can just group all remaining boxes,
+     * regardless of how many bands, into one final append to the list.
+     */
+
+    if ((r1 != r1_end) && append_non1)
+    {
+        /* Do first non_overlap1Func call, which may be able to coalesce */
+        FIND_BAND (r1, r1_band_end, r1_end, r1y1);
+
+        cur_band = new_reg->data->numRects;
+
+        if (!pixman_region_append_non_o (new_reg,
+                                         r1, r1_band_end,
+                                         MAX (r1y1, ybot), r1->y2))
+    {
+        goto bail;
+    }
+
+        COALESCE (new_reg, prev_band, cur_band);
+
+        /* Just append the rest of the boxes  */
+        APPEND_REGIONS (new_reg, r1_band_end, r1_end);
+    }
+    else if ((r2 != r2_end) && append_non2)
+    {
+        /* Do first non_overlap2Func call, which may be able to coalesce */
+        FIND_BAND (r2, r2_band_end, r2_end, r2y1);
+
+    cur_band = new_reg->data->numRects;
+
+        if (!pixman_region_append_non_o (new_reg,
+                                         r2, r2_band_end,
+                                         MAX (r2y1, ybot), r2->y2))
+    {
+        goto bail;
+    }
+
+        COALESCE (new_reg, prev_band, cur_band);
+
+        /* Append rest of boxes */
+        APPEND_REGIONS (new_reg, r2_band_end, r2_end);
+    }
+
+    free (old_data);
+
+    if (!(numRects = new_reg->data->numRects))
+    {
+        FREE_DATA (new_reg);
+        new_reg->data = pixman_region_empty_data;
+    }
+    else if (numRects == 1)
+    {
+        new_reg->extents = *PIXREGION_BOXPTR (new_reg);
+        FREE_DATA (new_reg);
+        new_reg->data = (region_data_type_t *)NULL;
+    }
+    else
+    {
+        DOWNSIZE (new_reg, numRects);
+    }
+
+    return TRUE;
+
+bail:
+    free (old_data);
+
+    return pixman_break (new_reg);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_set_extents --
+ * Reset the extents of a region to what they should be. Called by
+ * pixman_region_subtract and pixman_region_intersect as they can't
+ *      figure it out along the way or do so easily, as pixman_region_union can.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The region's 'extents' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+pixman_set_extents (region_type_t *region)
+{
+    box_type_t *box, *box_end;
+
+    if (!region->data)
+    return;
+
+    if (!region->data->size)
+    {
+        region->extents.x2 = region->extents.x1;
+        region->extents.y2 = region->extents.y1;
+        return;
+    }
+
+    box = PIXREGION_BOXPTR (region);
+    box_end = PIXREGION_END (region);
+
+    /*
+     * Since box is the first rectangle in the region, it must have the
+     * smallest y1 and since box_end is the last rectangle in the region,
+     * it must have the largest y2, because of banding. Initialize x1 and
+     * x2 from  box and box_end, resp., as good things to initialize them
+     * to...
+     */
+    region->extents.x1 = box->x1;
+    region->extents.y1 = box->y1;
+    region->extents.x2 = box_end->x2;
+    region->extents.y2 = box_end->y2;
+
+    critical_if_fail (region->extents.y1 < region->extents.y2);
+
+    while (box <= box_end)
+    {
+        if (box->x1 < region->extents.x1)
+        region->extents.x1 = box->x1;
+        if (box->x2 > region->extents.x2)
+        region->extents.x2 = box->x2;
+        box++;
+    }
+
+    critical_if_fail (region->extents.x1 < region->extents.x2);
+}
+
+/*======================================================================
+ *     Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_region_intersect_o --
+ * Handle an overlapping band for pixman_region_intersect.
+ *
+ * Results:
+ * TRUE if successful.
+ *
+ * Side Effects:
+ * Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static pixman_bool_t
+pixman_region_intersect_o (region_type_t *region,
+                           box_type_t *   r1,
+                           box_type_t *   r1_end,
+                           box_type_t *   r2,
+                           box_type_t *   r2_end,
+                           int            y1,
+                           int            y2)
+{
+    int x1;
+    int x2;
+    box_type_t *        next_rect;
+
+    next_rect = PIXREGION_TOP (region);
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (r1 != r1_end && r2 != r2_end);
+
+    do
+    {
+        x1 = MAX (r1->x1, r2->x1);
+        x2 = MIN (r1->x2, r2->x2);
+
+        /*
+     * If there's any overlap between the two rectangles, add that
+     * overlap to the new region.
+     */
+        if (x1 < x2)
+        NEWRECT (region, next_rect, x1, y1, x2, y2);
+
+        /*
+     * Advance the pointer(s) with the leftmost right side, since the next
+     * rectangle on that list may still overlap the other region's
+     * current rectangle.
+     */
+        if (r1->x2 == x2)
+        {
+            r1++;
+    }
+        if (r2->x2 == x2)
+        {
+            r2++;
+    }
+    }
+    while ((r1 != r1_end) && (r2 != r2_end));
+
+    return TRUE;
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_intersect) (region_type_t *     new_reg,
+                     region_type_t *        reg1,
+                     region_type_t *        reg2)
+{
+    GOOD (reg1);
+    GOOD (reg2);
+    GOOD (new_reg);
+
+    /* check for trivial reject */
+    if (PIXREGION_NIL (reg1) || PIXREGION_NIL (reg2) ||
+        !EXTENTCHECK (&reg1->extents, &reg2->extents))
+    {
+        /* Covers about 20% of all cases */
+        FREE_DATA (new_reg);
+        new_reg->extents.x2 = new_reg->extents.x1;
+        new_reg->extents.y2 = new_reg->extents.y1;
+        if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2))
+        {
+            new_reg->data = pixman_broken_data;
+            return FALSE;
+    }
+        else
+    {
+        new_reg->data = pixman_region_empty_data;
+    }
+    }
+    else if (!reg1->data && !reg2->data)
+    {
+        /* Covers about 80% of cases that aren't trivially rejected */
+        new_reg->extents.x1 = MAX (reg1->extents.x1, reg2->extents.x1);
+        new_reg->extents.y1 = MAX (reg1->extents.y1, reg2->extents.y1);
+        new_reg->extents.x2 = MIN (reg1->extents.x2, reg2->extents.x2);
+        new_reg->extents.y2 = MIN (reg1->extents.y2, reg2->extents.y2);
+
+        FREE_DATA (new_reg);
+
+    new_reg->data = (region_data_type_t *)NULL;
+    }
+    else if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->extents))
+    {
+        return PREFIX (_copy) (new_reg, reg1);
+    }
+    else if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->extents))
+    {
+        return PREFIX (_copy) (new_reg, reg2);
+    }
+    else if (reg1 == reg2)
+    {
+        return PREFIX (_copy) (new_reg, reg1);
+    }
+    else
+    {
+        /* General purpose intersection */
+
+        if (!pixman_op (new_reg, reg1, reg2, pixman_region_intersect_o, FALSE, FALSE))
+        return FALSE;
+
+        pixman_set_extents (new_reg);
+    }
+
+    GOOD (new_reg);
+    return(TRUE);
+}
+
+#define MERGERECT(r)                   \
+    do                           \
+    {                         \
+        if (r->x1 <= x2)                  \
+    {                      \
+            /* Merge with current rectangle */           \
+            if (x2 < r->x2)                  \
+        x2 = r->x2;                 \
+    }                      \
+    else                      \
+    {                      \
+            /* Add current rectangle, start new one */         \
+            NEWRECT (region, next_rect, x1, y1, x2, y2);    \
+            x1 = r->x1;                   \
+            x2 = r->x2;                   \
+    }                      \
+        r++;                        \
+    } while (0)
+
+/*======================================================================
+ *     Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_region_union_o --
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * TRUE if successful.
+ *
+ * Side Effects:
+ * region is overwritten.
+ * overlap is set to TRUE if any boxes overlap.
+ *
+ *-----------------------------------------------------------------------
+ */
+static pixman_bool_t
+pixman_region_union_o (region_type_t *region,
+               box_type_t *   r1,
+               box_type_t *   r1_end,
+               box_type_t *   r2,
+               box_type_t *   r2_end,
+               int            y1,
+               int            y2)
+{
+    box_type_t *next_rect;
+    int x1;            /* left and right side of current union */
+    int x2;
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (r1 != r1_end && r2 != r2_end);
+
+    next_rect = PIXREGION_TOP (region);
+
+    /* Start off current rectangle */
+    if (r1->x1 < r2->x1)
+    {
+        x1 = r1->x1;
+        x2 = r1->x2;
+        r1++;
+    }
+    else
+    {
+        x1 = r2->x1;
+        x2 = r2->x2;
+        r2++;
+    }
+    while (r1 != r1_end && r2 != r2_end)
+    {
+        if (r1->x1 < r2->x1)
+        MERGERECT (r1);
+    else
+        MERGERECT (r2);
+    }
+
+    /* Finish off whoever (if any) is left */
+    if (r1 != r1_end)
+    {
+        do
+        {
+            MERGERECT (r1);
+    }
+        while (r1 != r1_end);
+    }
+    else if (r2 != r2_end)
+    {
+        do
+        {
+            MERGERECT (r2);
+    }
+        while (r2 != r2_end);
+    }
+
+    /* Add current rectangle */
+    NEWRECT (region, next_rect, x1, y1, x2, y2);
+
+    return TRUE;
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX(_intersect_rect) (region_type_t *dest,
+             region_type_t *source,
+             int x, int y,
+             unsigned int width,
+             unsigned int height)
+{
+    region_type_t region;
+
+    region.data = NULL;
+    region.extents.x1 = x;
+    region.extents.y1 = y;
+    region.extents.x2 = x + width;
+    region.extents.y2 = y + height;
+
+    return PREFIX(_intersect) (dest, source, &region);
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_union) (region_type_t *new_reg,
+                 region_type_t *reg1,
+                 region_type_t *reg2);
+
+/* Convenience function for performing union of region with a
+ * single rectangle
+ */
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_union_rect) (region_type_t *dest,
+                      region_type_t *source,
+                      int            x,
+              int            y,
+                      unsigned int   width,
+              unsigned int   height)
+{
+    region_type_t region;
+
+    region.extents.x1 = x;
+    region.extents.y1 = y;
+    region.extents.x2 = x + width;
+    region.extents.y2 = y + height;
+
+    if (!GOOD_RECT (&region.extents))
+    {
+        if (BAD_RECT (&region.extents))
+            _pixman_log_error (FUNC, "Invalid rectangle passed");
+    return PREFIX (_copy) (dest, source);
+    }
+
+    region.data = NULL;
+
+    return PREFIX (_union) (dest, source, &region);
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_union) (region_type_t *new_reg,
+                 region_type_t *reg1,
+                 region_type_t *reg2)
+{
+    /* Return TRUE if some overlap
+     * between reg1, reg2
+     */
+    GOOD (reg1);
+    GOOD (reg2);
+    GOOD (new_reg);
+
+    /*  checks all the simple cases */
+
+    /*
+     * Region 1 and 2 are the same
+     */
+    if (reg1 == reg2)
+        return PREFIX (_copy) (new_reg, reg1);
+
+    /*
+     * Region 1 is empty
+     */
+    if (PIXREGION_NIL (reg1))
+    {
+        if (PIXREGION_NAR (reg1))
+        return pixman_break (new_reg);
+
+        if (new_reg != reg2)
+        return PREFIX (_copy) (new_reg, reg2);
+
+    return TRUE;
+    }
+
+    /*
+     * Region 2 is empty
+     */
+    if (PIXREGION_NIL (reg2))
+    {
+        if (PIXREGION_NAR (reg2))
+        return pixman_break (new_reg);
+
+    if (new_reg != reg1)
+        return PREFIX (_copy) (new_reg, reg1);
+
+    return TRUE;
+    }
+
+    /*
+     * Region 1 completely subsumes region 2
+     */
+    if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->extents))
+    {
+        if (new_reg != reg1)
+        return PREFIX (_copy) (new_reg, reg1);
+
+    return TRUE;
+    }
+
+    /*
+     * Region 2 completely subsumes region 1
+     */
+    if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->extents))
+    {
+        if (new_reg != reg2)
+        return PREFIX (_copy) (new_reg, reg2);
+
+    return TRUE;
+    }
+
+    if (!pixman_op (new_reg, reg1, reg2, pixman_region_union_o, TRUE, TRUE))
+    return FALSE;
+
+    new_reg->extents.x1 = MIN (reg1->extents.x1, reg2->extents.x1);
+    new_reg->extents.y1 = MIN (reg1->extents.y1, reg2->extents.y1);
+    new_reg->extents.x2 = MAX (reg1->extents.x2, reg2->extents.x2);
+    new_reg->extents.y2 = MAX (reg1->extents.y2, reg2->extents.y2);
+
+    GOOD (new_reg);
+
+    return TRUE;
+}
+
+/*======================================================================
+ *                Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_region_subtract_o --
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * TRUE if successful.
+ *
+ * Side Effects:
+ * region may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static pixman_bool_t
+pixman_region_subtract_o (region_type_t * region,
+              box_type_t *    r1,
+              box_type_t *    r1_end,
+              box_type_t *    r2,
+              box_type_t *    r2_end,
+              int             y1,
+              int             y2)
+{
+    box_type_t *        next_rect;
+    int x1;
+
+    x1 = r1->x1;
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (r1 != r1_end && r2 != r2_end);
+
+    next_rect = PIXREGION_TOP (region);
+
+    do
+    {
+        if (r2->x2 <= x1)
+        {
+            /*
+         * Subtrahend entirely to left of minuend: go to next subtrahend.
+         */
+            r2++;
+    }
+        else if (r2->x1 <= x1)
+        {
+            /*
+         * Subtrahend precedes minuend: nuke left edge of minuend.
+         */
+            x1 = r2->x2;
+            if (x1 >= r1->x2)
+            {
+                /*
+         * Minuend completely covered: advance to next minuend and
+         * reset left fence to edge of new minuend.
+         */
+                r1++;
+                if (r1 != r1_end)
+            x1 = r1->x1;
+        }
+            else
+            {
+                /*
+         * Subtrahend now used up since it doesn't extend beyond
+         * minuend
+         */
+                r2++;
+        }
+    }
+        else if (r2->x1 < r1->x2)
+        {
+            /*
+         * Left part of subtrahend covers part of minuend: add uncovered
+         * part of minuend to region and skip to next subtrahend.
+         */
+            critical_if_fail (x1 < r2->x1);
+            NEWRECT (region, next_rect, x1, y1, r2->x1, y2);
+
+            x1 = r2->x2;
+            if (x1 >= r1->x2)
+            {
+                /*
+         * Minuend used up: advance to new...
+         */
+                r1++;
+                if (r1 != r1_end)
+            x1 = r1->x1;
+        }
+            else
+            {
+                /*
+         * Subtrahend used up
+         */
+                r2++;
+        }
+    }
+        else
+        {
+            /*
+         * Minuend used up: add any remaining piece before advancing.
+         */
+            if (r1->x2 > x1)
+        NEWRECT (region, next_rect, x1, y1, r1->x2, y2);
+
+            r1++;
+
+        if (r1 != r1_end)
+        x1 = r1->x1;
+    }
+    }
+    while ((r1 != r1_end) && (r2 != r2_end));
+
+    /*
+     * Add remaining minuend rectangles to region.
+     */
+    while (r1 != r1_end)
+    {
+        critical_if_fail (x1 < r1->x2);
+
+        NEWRECT (region, next_rect, x1, y1, r1->x2, y2);
+
+        r1++;
+        if (r1 != r1_end)
+        x1 = r1->x1;
+    }
+    return TRUE;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_region_subtract --
+ * Subtract reg_s from reg_m and leave the result in reg_d.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Results:
+ * TRUE if successful.
+ *
+ * Side Effects:
+ * reg_d is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_subtract) (region_type_t *reg_d,
+                    region_type_t *reg_m,
+                    region_type_t *reg_s)
+{
+    GOOD (reg_m);
+    GOOD (reg_s);
+    GOOD (reg_d);
+
+    /* check for trivial rejects */
+    if (PIXREGION_NIL (reg_m) || PIXREGION_NIL (reg_s) ||
+        !EXTENTCHECK (&reg_m->extents, &reg_s->extents))
+    {
+        if (PIXREGION_NAR (reg_s))
+        return pixman_break (reg_d);
+
+        return PREFIX (_copy) (reg_d, reg_m);
+    }
+    else if (reg_m == reg_s)
+    {
+        FREE_DATA (reg_d);
+        reg_d->extents.x2 = reg_d->extents.x1;
+        reg_d->extents.y2 = reg_d->extents.y1;
+        reg_d->data = pixman_region_empty_data;
+
+        return TRUE;
+    }
+
+    /* Add those rectangles in region 1 that aren't in region 2,
+       do yucky subtraction for overlaps, and
+       just throw away rectangles in region 2 that aren't in region 1 */
+    if (!pixman_op (reg_d, reg_m, reg_s, pixman_region_subtract_o, TRUE, FALSE))
+    return FALSE;
+
+    /*
+     * Can't alter reg_d's extents before we call pixman_op because
+     * it might be one of the source regions and pixman_op depends
+     * on the extents of those regions being unaltered. Besides, this
+     * way there's no checking against rectangles that will be nuked
+     * due to coalescing, so we have to examine fewer rectangles.
+     */
+    pixman_set_extents (reg_d);
+    GOOD (reg_d);
+    return TRUE;
+}
+#if 0
+/*======================================================================
+ *     Region Inversion
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pixman_region_inverse --
+ * Take a region and a box and return a region that is everything
+ * in the box but not in the region. The careful reader will note
+ * that this is the same as subtracting the region from the box...
+ *
+ * Results:
+ * TRUE.
+ *
+ * Side Effects:
+ * new_reg is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_inverse) (region_type_t *new_reg,  /* Destination region */
+           region_type_t *reg1,     /* Region to invert */
+           box_type_t *   inv_rect) /* Bounding box for inversion */
+{
+    region_type_t inv_reg; /* Quick and dirty region made from the
+                * bounding box */
+    GOOD (reg1);
+    GOOD (new_reg);
+
+    /* check for trivial rejects */
+    if (PIXREGION_NIL (reg1) || !EXTENTCHECK (inv_rect, &reg1->extents))
+    {
+        if (PIXREGION_NAR (reg1))
+        return pixman_break (new_reg);
+
+        new_reg->extents = *inv_rect;
+        FREE_DATA (new_reg);
+        new_reg->data = (region_data_type_t *)NULL;
+
+        return TRUE;
+    }
+
+    /* Add those rectangles in region 1 that aren't in region 2,
+     * do yucky subtraction for overlaps, and
+     * just throw away rectangles in region 2 that aren't in region 1
+     */
+    inv_reg.extents = *inv_rect;
+    inv_reg.data = (region_data_type_t *)NULL;
+    if (!pixman_op (new_reg, &inv_reg, reg1, pixman_region_subtract_o, TRUE, FALSE))
+    return FALSE;
+
+    /*
+     * Can't alter new_reg's extents before we call pixman_op because
+     * it might be one of the source regions and pixman_op depends
+     * on the extents of those regions being unaltered. Besides, this
+     * way there's no checking against rectangles that will be nuked
+     * due to coalescing, so we have to examine fewer rectangles.
+     */
+    pixman_set_extents (new_reg);
+    GOOD (new_reg);
+    return TRUE;
+}
+#endif
+/* In time O(log n), locate the first box whose y2 is greater than y.
+ * Return @end if no such box exists.
+ */
+static box_type_t *
+find_box_for_y (box_type_t *begin, box_type_t *end, int y)
+{
+    box_type_t *mid;
+
+    if (end == begin)
+    return end;
+
+    if (end - begin == 1)
+    {
+    if (begin->y2 > y)
+        return begin;
+    else
+        return end;
+    }
+
+    mid = begin + (end - begin) / 2;
+    if (mid->y2 > y)
+    {
+    /* If no box is found in [begin, mid], the function
+     * will return @mid, which is then known to be the
+     * correct answer.
+     */
+    return find_box_for_y (begin, mid, y);
+    }
+    else
+    {
+    return find_box_for_y (mid, end, y);
+    }
+}
+
+/*
+ *   rect_in(region, rect)
+ *   This routine takes a pointer to a region and a pointer to a box
+ *   and determines if the box is outside/inside/partly inside the region.
+ *
+ *   The idea is to travel through the list of rectangles trying to cover the
+ *   passed box with them. Anytime a piece of the rectangle isn't covered
+ *   by a band of rectangles, part_out is set TRUE. Any time a rectangle in
+ *   the region covers part of the box, part_in is set TRUE. The process ends
+ *   when either the box has been completely covered (we reached a band that
+ *   doesn't overlap the box, part_in is TRUE and part_out is false), the
+ *   box has been partially covered (part_in == part_out == TRUE -- because of
+ *   the banding, the first time this is true we know the box is only
+ *   partially in the region) or is outside the region (we reached a band
+ *   that doesn't overlap the box at all and part_in is false)
+ */
+PIXMAN_EXPORT pixman_region_overlap_t
+PREFIX (_contains_rectangle) (region_type_t *  region,
+                  box_type_t *     prect)
+{
+    box_type_t *     pbox;
+    box_type_t *     pbox_end;
+    int part_in, part_out;
+    int numRects;
+    int x, y;
+
+    GOOD (region);
+
+    numRects = PIXREGION_NUMRECTS (region);
+
+    /* useful optimization */
+    if (!numRects || !EXTENTCHECK (&region->extents, prect))
+    return(PIXMAN_REGION_OUT);
+
+    if (numRects == 1)
+    {
+        /* We know that it must be PIXMAN_REGION_IN or PIXMAN_REGION_PART */
+        if (SUBSUMES (&region->extents, prect))
+        return(PIXMAN_REGION_IN);
+        else
+        return(PIXMAN_REGION_PART);
+    }
+
+    part_out = FALSE;
+    part_in = FALSE;
+
+    /* (x,y) starts at upper left of rect, moving to the right and down */
+    x = prect->x1;
+    y = prect->y1;
+
+    /* can stop when both part_out and part_in are TRUE, or we reach prect->y2 */
+    for (pbox = PIXREGION_BOXPTR (region), pbox_end = pbox + numRects;
+     pbox != pbox_end;
+     pbox++)
+    {
+    /* getting up to speed or skipping remainder of band */
+    if (pbox->y2 <= y)
+    {
+        if ((pbox = find_box_for_y (pbox, pbox_end, y)) == pbox_end)
+        break;
+    }
+
+        if (pbox->y1 > y)
+        {
+            part_out = TRUE;     /* missed part of rectangle above */
+            if (part_in || (pbox->y1 >= prect->y2))
+        break;
+            y = pbox->y1;       /* x guaranteed to be == prect->x1 */
+    }
+
+        if (pbox->x2 <= x)
+        continue;           /* not far enough over yet */
+
+        if (pbox->x1 > x)
+        {
+            part_out = TRUE;     /* missed part of rectangle to left */
+            if (part_in)
+        break;
+    }
+
+        if (pbox->x1 < prect->x2)
+        {
+            part_in = TRUE;      /* definitely overlap */
+            if (part_out)
+        break;
+    }
+
+        if (pbox->x2 >= prect->x2)
+        {
+            y = pbox->y2;       /* finished with this band */
+            if (y >= prect->y2)
+        break;
+            x = prect->x1;      /* reset x out to left again */
+    }
+        else
+        {
+            /*
+         * Because boxes in a band are maximal width, if the first box
+         * to overlap the rectangle doesn't completely cover it in that
+         * band, the rectangle must be partially out, since some of it
+         * will be uncovered in that band. part_in will have been set true
+         * by now...
+         */
+            part_out = TRUE;
+            break;
+    }
+    }
+
+    if (part_in)
+    {
+        if (y < prect->y2)
+        return PIXMAN_REGION_PART;
+        else
+        return PIXMAN_REGION_IN;
+    }
+    else
+    {
+        return PIXMAN_REGION_OUT;
+    }
+}
+
+/* PREFIX(_translate) (region, x, y)
+ * translates in place
+ */
+
+PIXMAN_EXPORT void
+PREFIX (_translate) (region_type_t *region, int x, int y)
+{
+    overflow_int_t x1, x2, y1, y2;
+    int nbox;
+    box_type_t * pbox;
+
+    GOOD (region);
+    region->extents.x1 = x1 = region->extents.x1 + x;
+    region->extents.y1 = y1 = region->extents.y1 + y;
+    region->extents.x2 = x2 = region->extents.x2 + x;
+    region->extents.y2 = y2 = region->extents.y2 + y;
+
+    if (((x1 - PIXMAN_REGION_MIN) | (y1 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x2) | (PIXMAN_REGION_MAX - y2)) >= 0)
+    {
+        if (region->data && (nbox = region->data->numRects))
+        {
+            for (pbox = PIXREGION_BOXPTR (region); nbox--; pbox++)
+            {
+                pbox->x1 += x;
+                pbox->y1 += y;
+                pbox->x2 += x;
+                pbox->y2 += y;
+        }
+    }
+        return;
+    }
+
+    if (((x2 - PIXMAN_REGION_MIN) | (y2 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x1) | (PIXMAN_REGION_MAX - y1)) <= 0)
+    {
+        region->extents.x2 = region->extents.x1;
+        region->extents.y2 = region->extents.y1;
+        FREE_DATA (region);
+        region->data = pixman_region_empty_data;
+        return;
+    }
+
+    if (x1 < PIXMAN_REGION_MIN)
+    region->extents.x1 = PIXMAN_REGION_MIN;
+    else if (x2 > PIXMAN_REGION_MAX)
+    region->extents.x2 = PIXMAN_REGION_MAX;
+
+    if (y1 < PIXMAN_REGION_MIN)
+    region->extents.y1 = PIXMAN_REGION_MIN;
+    else if (y2 > PIXMAN_REGION_MAX)
+    region->extents.y2 = PIXMAN_REGION_MAX;
+
+    if (region->data && (nbox = region->data->numRects))
+    {
+        box_type_t * pbox_out;
+
+        for (pbox_out = pbox = PIXREGION_BOXPTR (region); nbox--; pbox++)
+        {
+            pbox_out->x1 = x1 = pbox->x1 + x;
+            pbox_out->y1 = y1 = pbox->y1 + y;
+            pbox_out->x2 = x2 = pbox->x2 + x;
+            pbox_out->y2 = y2 = pbox->y2 + y;
+
+            if (((x2 - PIXMAN_REGION_MIN) | (y2 - PIXMAN_REGION_MIN) |
+                 (PIXMAN_REGION_MAX - x1) | (PIXMAN_REGION_MAX - y1)) <= 0)
+            {
+                region->data->numRects--;
+                continue;
+        }
+
+            if (x1 < PIXMAN_REGION_MIN)
+        pbox_out->x1 = PIXMAN_REGION_MIN;
+            else if (x2 > PIXMAN_REGION_MAX)
+        pbox_out->x2 = PIXMAN_REGION_MAX;
+
+            if (y1 < PIXMAN_REGION_MIN)
+        pbox_out->y1 = PIXMAN_REGION_MIN;
+            else if (y2 > PIXMAN_REGION_MAX)
+        pbox_out->y2 = PIXMAN_REGION_MAX;
+
+            pbox_out++;
+    }
+
+        if (pbox_out != pbox)
+        {
+            if (region->data->numRects == 1)
+            {
+                region->extents = *PIXREGION_BOXPTR (region);
+                FREE_DATA (region);
+                region->data = (region_data_type_t *)NULL;
+        }
+            else
+        {
+        pixman_set_extents (region);
+        }
+    }
+    }
+
+    GOOD (region);
+}
+
+PIXMAN_EXPORT int
+PREFIX (_not_empty) (region_type_t * region)
+{
+    GOOD (region);
+
+    return(!PIXREGION_NIL (region));
+}
+
+PIXMAN_EXPORT box_type_t *
+PREFIX (_extents) (region_type_t * region)
+{
+    GOOD (region);
+
+    return(&region->extents);
+}
+
+typedef region_type_t VRegionPrivate;
+
+#include "vregion.h"
+
+static VRegionPrivate regionPrivate = {{0,0,0,0}, NULL};
+const VRegion::VRegionData VRegion::shared_empty = {RefCount(-1), &regionPrivate};
+
+inline VRect box_to_rect(box_type_t *box)
+{
+    return VRect(box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1);
+}
+
+void VRegion::cleanUp(VRegion::VRegionData *x)
+{
+    if (x->rgn) {
+        PREFIX(_fini)(x->rgn);
+        delete x->rgn;
+    }
+    delete x;
+}
+
+void VRegion::detach()
+{
+    if (d->ref.isShared())
+        *this = copy();
+}
+
+VRegion VRegion::copy() const
+{
+    VRegion r;
+
+    r.d = new VRegionData;
+    r.d->rgn = new VRegionPrivate;
+    r.d->ref.setOwned();
+    PREFIX (_init) (r.d->rgn);
+    if (d != &shared_empty)
+        PREFIX(_copy)(r.d->rgn, d->rgn);
+    return r;
+}
+
+VRegion::VRegion()
+    : d(const_cast<VRegionData*>(&shared_empty))
+{
+}
+
+VRegion::VRegion(int x, int y, int w, int h)
+{
+    VRegion tmp(VRect(x, y, w, h));
+    tmp.d->ref.ref();
+    d = tmp.d;
+}
+
+VRegion::VRegion(const VRect &r)
+{
+    if (r.isEmpty()) {
+        d = const_cast<VRegionData*>(&shared_empty);
+    } else {
+        d = new VRegionData;
+        d->rgn = new VRegionPrivate;
+        d->ref.setOwned();
+        PREFIX(_init_rect)(d->rgn, r.left(), r.top(), r.width(), r.height());
+    }
+}
+
+VRegion::VRegion(const VRegion &r)
+{
+    d = r.d;
+    d->ref.ref();
+}
+
+VRegion &VRegion::operator=(const VRegion &r)
+{
+    r.d->ref.ref();
+    if (!d->ref.deref())
+        cleanUp(d);
+
+    d = r.d;
+    return *this;
+}
+
+inline VRegion &VRegion::operator=(VRegion &&other)
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+    d = other.d;
+    other.d = const_cast<VRegionData*>(&shared_empty);
+    return *this;
+}
+
+VRegion::~VRegion()
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+}
+
+bool VRegion::isEmpty() const
+{
+    return d == &shared_empty || !PREFIX(_not_empty)(d->rgn);
+}
+
+void VRegion::translate(const VPoint &p)
+{
+    if (p == VPoint() || isEmpty())
+        return;
+
+    detach();
+    PREFIX(_translate)(d->rgn, p.x(), p.y());
+}
+
+VRegion VRegion::translated(const VPoint &p) const
+{
+    VRegion ret(*this);
+    ret.translate(p);
+    return ret;
+}
+
+/*
+ * Returns \c true if this region is guaranteed to be fully contained in r.
+ */
+bool VRegion::within(const VRect &r1) const
+{
+    box_type_t *r2 = PREFIX(_extents)(d->rgn);
+
+    return r2->x1 >= r1.left() && r2->x2 <= r1.right()
+        && r2->y1 >= r1.top() && r2->y2 <= r1.bottom();
+}
+
+bool VRegion::contains(const VRect &r) const
+{
+    box_type_t box = {r.left(), r.top(), r.right(), r.bottom()};
+
+    pixman_region_overlap_t res = PREFIX (_contains_rectangle)(d->rgn, &box);
+    if (res == PIXMAN_REGION_IN)
+        return true;
+    return false;
+}
+
+VRegion VRegion::united(const VRect &r) const
+{
+    if (isEmpty())
+        return r;
+
+    if (contains(r)) {
+        return *this;
+    } else if (within(r)) {
+        return r;
+    } else {
+        VRegion result;
+        result.detach();
+        PREFIX(_union_rect)(result.d->rgn, d->rgn,
+                            r.left(), r.top(),
+                            r.width(), r.height());
+        return result;
+    }
+}
+
+VRegion VRegion::united(const VRegion &r) const
+{
+    if (isEmpty())
+        return r;
+    if (r.isEmpty())
+        return *this;
+    if (d == r.d ||
+        PREFIX(_equal)(d->rgn, r.d->rgn))
+        return *this;
+    VRegion result;
+    result.detach();
+    PREFIX(_union)(result.d->rgn, d->rgn, r.d->rgn);
+    return result;
+}
+
+VRegion VRegion::intersected(const VRect &r) const
+{
+    if (isEmpty() || r.isEmpty())
+        return VRegion();
+
+    /* this is fully contained in r */
+    if (within(r))
+        return *this;
+
+    /* r is fully contained in this */
+    if (contains(r))
+        return r;
+
+    VRegion result;
+    result.detach();
+    PREFIX(_intersect_rect)(result.d->rgn, d->rgn,
+                            r.left(), r.top(),
+                            r.width(), r.height());
+    return result;
+}
+
+VRegion VRegion::intersected(const VRegion &r) const
+{
+    if (isEmpty() || r.isEmpty())
+        return VRegion();
+
+    VRegion result;
+    result.detach();
+    PREFIX(_intersect)(result.d->rgn, d->rgn, r.d->rgn);
+
+    return result;
+}
+
+VRegion VRegion::subtracted(const VRegion &r) const
+{
+    if (isEmpty() || r.isEmpty())
+        return *this;
+    if (d == r.d ||
+        PREFIX(_equal)(d->rgn, r.d->rgn))
+        return VRegion();
+
+    VRegion result;
+    result.detach();
+    PREFIX(_subtract)(result.d->rgn, d->rgn, r.d->rgn);
+    return result;
+}
+
+int VRegion::rectCount() const
+{
+    if (isEmpty()) return 0;
+    return PREFIX(_n_rects)(d->rgn);
+}
+
+VRect VRegion::rectAt(int index) const
+{
+    VRegionPrivate *reg = d->rgn;
+    if (!reg)
+        return VRect();
+
+    box_type_t *box = PIXREGION_RECTS(reg) + index;
+
+    return box_to_rect(box);
+}
+
+VRegion VRegion::operator+(const VRect &r) const
+{ return united(r); }
+
+VRegion VRegion::operator+(const VRegion &r) const
+{ return united(r); }
+
+VRegion VRegion::operator-(const VRegion &r) const
+{ return subtracted(r); }
+
+VRegion& VRegion::operator+=(const VRect &r)
+{
+    if (isEmpty())
+        return *this = r;
+    if (r.isEmpty())
+        return *this;
+
+    if (contains(r)) {
+        return *this;
+    } else if (within(r)) {
+        return *this = r;
+    } else {
+        detach();
+        PREFIX(_union_rect)(d->rgn, d->rgn,
+                            r.left(), r.top(),
+                            r.width(), r.height());
+        return *this;
+    }
+}
+
+VRegion& VRegion::operator+=(const VRegion &r)
+{
+    if (isEmpty())
+        return *this = r;
+    if (r.isEmpty())
+        return *this;
+    if (d == r.d ||
+        PREFIX(_equal)(d->rgn, r.d->rgn))
+        return *this;
+
+    detach();
+    PREFIX(_union)(d->rgn, d->rgn, r.d->rgn);
+    return *this;
+}
+
+VRegion& VRegion::operator-=(const VRegion &r)
+{ return *this = *this - r; }
+
+bool VRegion::operator==(const VRegion &r) const
+{
+    if (isEmpty())
+        return r.isEmpty();
+    if (r.isEmpty())
+        return isEmpty();
+
+    if (d == r.d)
+        return true;
+    else
+        return PREFIX(_equal)(d->rgn, r.d->rgn);
+}
+
+VRect VRegion::boundingRect() const noexcept
+{
+    if (isEmpty())
+        return VRect();
+    return box_to_rect(&d->rgn->extents);
+}
+
+inline bool rect_intersects(const VRect &r1, const VRect &r2)
+{
+    return (r1.right() >= r2.left() && r1.left() <= r2.right() &&
+            r1.bottom() >= r2.top() && r1.top() <= r2.bottom());
+}
+
+bool VRegion::intersects(const VRegion &r) const
+{
+    if (isEmpty() || r.isEmpty())
+        return false;
+
+    return PREFIX(_intersects)(d->rgn, r.d->rgn);
+}
+
+VDebug& operator<<(VDebug& os, const VRegion& o)
+{
+    os<<"[REGION: "<<"[bbox = "<<o.boundingRect()<<"]";
+    os<<"[rectCount = "<<o.rectCount()<<"]";
+    os<<"[rects = ";
+    for(int i=0; i< o.rectCount(); i++) {
+        os<<o.rectAt(i);
+    }
+    os<<"]"<<"]";
+    return os;
+
+}
+
diff --git a/src/vector/vregion.h b/src/vector/vregion.h
new file mode 100644 (file)
index 0000000..75e7c38
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef VREGION_H
+#define VREGION_H
+#include"vdebug.h"
+#include <vglobal.h>
+#include<vrect.h>
+#include<vpoint.h>
+#include<utility>
+
+typedef struct pixman_region  region_type_t;
+typedef region_type_t VRegionPrivate;
+
+class  VRegion
+{
+public:
+    VRegion();
+    VRegion(int x, int y, int w, int h);
+    VRegion(const VRect &r);
+    VRegion(const VRegion &region);
+    VRegion(VRegion &&other): d(other.d) { other.d = const_cast<VRegionData*>(&shared_empty); }
+    ~VRegion();
+    VRegion &operator=(const VRegion &);
+    VRegion &operator=(VRegion &&);
+    bool isEmpty() const;
+    bool contains(const VRect &r) const;
+    VRegion united(const VRect &r) const;
+    VRegion united(const VRegion &r) const;
+    VRegion intersected(const VRect &r) const;
+    VRegion intersected(const VRegion &r) const;
+    VRegion subtracted(const VRegion &r) const;
+    void translate(const VPoint &p);
+    inline void translate(int dx, int dy);
+    VRegion translated(const VPoint &p) const;
+    inline VRegion translated(int dx, int dy) const;
+    int rectCount() const;
+    VRect rectAt(int index) const;
+
+    VRegion operator+(const VRect &r) const;
+    VRegion operator+(const VRegion &r) const;
+    VRegion operator-(const VRegion &r) const;
+    VRegion& operator+=(const VRect &r);
+    VRegion& operator+=(const VRegion &r);
+    VRegion& operator-=(const VRegion &r);
+
+    VRect boundingRect() const noexcept;
+    bool intersects(const VRegion &region) const;
+
+    bool operator==(const VRegion &r) const;
+    inline bool operator!=(const VRegion &r) const { return !(operator==(r)); }
+    friend VDebug& operator<<(VDebug& os, const VRegion& o);
+private:
+    bool within(const VRect &r) const;
+    VRegion copy() const;
+    void detach();
+
+    struct VRegionData {
+        RefCount ref;
+        VRegionPrivate *rgn;
+    };
+
+    struct VRegionData *d;
+    static const struct VRegionData shared_empty;
+    static void cleanUp(VRegionData *x);
+};
+inline void VRegion::translate(int dx, int dy)
+{
+    translate(VPoint(dx,dy));
+}
+
+inline VRegion VRegion::translated(int dx, int dy) const
+{
+    return translated(VPoint(dx,dy));
+}
+#endif //VREGION_H
diff --git a/src/vector/vrle.cpp b/src/vector/vrle.cpp
new file mode 100644 (file)
index 0000000..a7a70de
--- /dev/null
@@ -0,0 +1,810 @@
+#include "vrle.h"
+#include"vglobal.h"
+#include<vrect.h>
+#include<cstdlib>
+#include<vector>
+#include<array>
+#include<algorithm>
+#include"vdebug.h"
+#include"vregion.h"
+
+struct VRleHelper
+{
+   ushort        alloc;
+   ushort        size;
+   VRle::Span  *spans;
+};
+
+#define VMIN(a,b) ((a) < (b) ? (a) : (b))
+#define VMAX(a,b) ((a) > (b) ? (a) : (b))
+
+static inline uchar
+divBy255(int x) { return (x + (x>>8) + 0x80) >> 8; }
+
+/*
+ * This function will clip a rle list with another rle object
+ * tmp_clip  : The rle list that will be use to clip the rle
+ * tmp_obj   : holds the list of spans that has to be clipped
+ * result    : will hold the result after the processing
+ * NOTE: if the algorithm runs out of the result buffer list
+ *       it will stop and update the tmp_obj with the span list
+ *       that are yet to be processed as well as the tpm_clip object
+ *       with the unprocessed clip spans.
+ */
+static void
+rleIntersectWithRle(VRleHelper *tmp_clip,
+                    int clip_offset_x,
+                    int clip_offset_y,
+                    VRleHelper *tmp_obj,
+                    VRleHelper *result)
+{
+    VRle::Span *out = result->spans;
+    int available = result->alloc;
+    VRle::Span *spans = tmp_obj->spans;
+    VRle::Span *end = tmp_obj->spans + tmp_obj->size;
+    VRle::Span *clipSpans = tmp_clip->spans;
+    VRle::Span *clipEnd = tmp_clip->spans + tmp_clip->size;
+    int sx1, sx2, cx1, cx2, x, len;
+
+
+   while (available && spans < end )
+     {
+        if (clipSpans >= clipEnd)
+          {
+             spans = end;
+             break;
+          }
+        if ((clipSpans->y + clip_offset_y) > spans->y)
+          {
+             ++spans;
+             continue;
+          }
+        if (spans->y != (clipSpans->y + clip_offset_y))
+          {
+             ++clipSpans;
+             continue;
+          }
+        //assert(spans->y == (clipSpans->y + clip_offset_y));
+        sx1 = spans->x;
+        sx2 = sx1 + spans->len;
+        cx1 = (clipSpans->x + clip_offset_x);
+        cx2 = cx1 + clipSpans->len;
+
+        if (cx1 < sx1 && cx2 < sx1)
+          {
+             ++clipSpans;
+             continue;
+          }
+        else if (sx1 < cx1 && sx2 < cx1)
+          {
+             ++spans;
+             continue;
+          }
+        x = VMAX(sx1, cx1);
+        len = VMIN(sx2, cx2) - x;
+        if (len)
+          {
+             out->x = VMAX(sx1, cx1);
+             out->len = ( VMIN(sx2, cx2) - out->x);
+             out->y = spans->y;
+             out->coverage = divBy255(spans->coverage * clipSpans->coverage);
+             ++out;
+             --available;
+          }
+        if (sx2 < cx2)
+          {
+             ++spans;
+          }
+        else
+          {
+             ++clipSpans;
+          }
+     }
+
+   // update the span list that yet to be processed
+   tmp_obj->spans = spans;
+   tmp_obj->size = end - spans;
+
+   // update the clip list that yet to be processed
+   tmp_clip->spans = clipSpans;
+   tmp_clip->size = clipEnd - clipSpans;
+
+   // update the result
+   result->size = result->alloc - available;
+}
+
+/*
+ * This function will clip a rle list with a given rect
+ * clip      : The clip rect that will be use to clip the rle
+ * tmp_obj   : holds the list of spans that has to be clipped
+ * result    : will hold the result after the processing
+ * NOTE: if the algorithm runs out of the result buffer list
+ *       it will stop and update the tmp_obj with the span list
+ *       that are yet to be processed
+ */
+static void
+rleIntersectWithRect(const VRect &clip,
+                     VRleHelper *tmp_obj,
+                     VRleHelper *result)
+{
+   VRle::Span *out = result->spans;
+   int available = result->alloc;
+   VRle::Span *spans = tmp_obj->spans;
+   VRle::Span *end = tmp_obj->spans + tmp_obj->size;
+   short minx, miny, maxx, maxy;
+
+   minx = clip.left();
+   miny = clip.top();
+   maxx = clip.right() - 1;
+   maxy = clip.bottom() - 1;
+
+   while (available && spans < end )
+     {
+        if (spans->y > maxy)
+          {
+             spans = end;// update spans so that we can breakout
+             break;
+          }
+        if (spans->y < miny
+            || spans->x > maxx
+            || spans->x + spans->len <= minx)
+          {
+             ++spans;
+             continue;
+          }
+        if (spans->x < minx)
+          {
+             out->len = VMIN(spans->len - (minx - spans->x), maxx - minx + 1);
+             out->x = minx;
+          }
+        else
+          {
+             out->x = spans->x;
+             out->len = VMIN(spans->len, (maxx - spans->x + 1));
+          }
+        if (out->len != 0)
+          {
+             out->y = spans->y;
+             out->coverage = spans->coverage;
+             ++out;
+          }
+        ++spans;
+        --available;
+     }
+
+   // update the span list that yet to be processed
+   tmp_obj->spans = spans;
+   tmp_obj->size = end - spans;
+
+   // update the result
+   result->size = result->alloc - available;
+}
+
+void drawSpanlineMul(VRle::Span *spans, int count, uchar *buffer, int offsetX)
+{
+    uchar *ptr;
+    while (count--) {
+        int x = spans->x + offsetX;
+        int l = spans->len;
+        ptr = buffer + x;
+        while (l--) {
+            uchar cov = *ptr;
+            *ptr++ = divBy255(spans->coverage * cov);
+        }
+        spans++;
+    }
+}
+
+void drawSpanline(VRle::Span *spans, int count, uchar *buffer, int offsetX)
+{
+    uchar *ptr;
+    while (count--) {
+        int x = spans->x + offsetX;
+        int l = spans->len;
+        ptr = buffer + x;
+        while (l--) {
+            *ptr++ = spans->coverage;
+        }
+        spans++;
+    }
+}
+
+int bufferToRle(uchar *buffer, int size, int offsetX, int y, VRle::Span *out)
+{
+    int count = 0;
+    uchar value = buffer[0];
+    int curIndex = 0;
+    for (int i = 0; i < size ; i++) {
+        uchar curValue = buffer[0];
+        if (value != curValue) {
+            out->y = y;
+            out->x = offsetX + curIndex;
+            out->len = i - curIndex;
+            out->coverage = value;
+            out++;
+            curIndex = i;
+            value = curValue;
+            count++;
+        }
+        buffer++;
+    }
+    out->y = y;
+    out->x = offsetX + curIndex;
+    out->len = size - curIndex;
+    out->coverage = value;
+    count++;
+    return count;
+}
+
+static void
+rleAddWithRle1(VRleHelper *tmp_clip,
+              VRleHelper *tmp_obj,
+              VRleHelper *result)
+{
+    std::array<VRle::Span,256> rleHolder;
+    VRle::Span *out = result->spans;
+    int available = result->alloc;
+    VRle::Span *spans = tmp_obj->spans;
+    VRle::Span *end = tmp_obj->spans + tmp_obj->size;
+    VRle::Span *clipSpans = tmp_clip->spans;
+    VRle::Span *clipEnd = tmp_clip->spans + tmp_clip->size;
+
+    while (available && spans < end && clipSpans < clipEnd) {
+        if (spans->y < clipSpans->y) {
+            *out++ = *spans++;
+            available--;
+        } else if (clipSpans->y < spans->y) {
+            *out++ = *clipSpans++;
+            available--;
+        } else { // same y
+            int y = spans->y;
+            VRle::Span *spanPtr = spans;
+            VRle::Span *clipPtr = clipSpans;
+
+            while (spanPtr < end && spanPtr->y == y) spanPtr++;
+            while (clipPtr < clipEnd && clipPtr->y == y) clipPtr++;
+
+            int spanLength = (spanPtr-1)->x + (spanPtr-1)->len - spans->x;
+            int clipLength = (clipPtr-1)->x + (clipPtr-1)->len - clipSpans->x;
+            int offsetX = VMIN(spans->x, clipSpans->x);
+            std::array<uchar,1024> array = {0};
+            drawSpanline(spans, (spanPtr - spans), array.data(), -offsetX);
+            drawSpanlineMul(clipSpans, (clipPtr - clipSpans), array.data(), -offsetX);
+            VRle::Span *rleHolderPtr = rleHolder.data();
+            int size = bufferToRle(array.data(), VMAX(spanLength, clipLength),
+                                   offsetX, y, rleHolderPtr);
+            if (available >= size) {
+                while(size--) {
+                    *out++ = *rleHolderPtr++;
+                    available--;
+                }
+            } else {
+                break;
+            }
+            spans = spanPtr;
+            clipSpans = clipPtr;
+        }
+    }
+    // update the span list that yet to be processed
+    tmp_obj->spans = spans;
+    tmp_obj->size = end - spans;
+
+    // update the clip list that yet to be processed
+    tmp_clip->spans = clipSpans;
+    tmp_clip->size = clipEnd - clipSpans;
+
+    // update the result
+    result->size = result->alloc - available;
+}
+
+
+class VRleImpl
+{
+public:
+    inline VRleImpl():m_bbox(),m_spans(), mOffset(), mBboxDirty(true){}
+    VRleImpl &operator=(const VRleImpl &);
+    void addSpan(const VRle::Span *span, int count);
+    void updateBbox();
+    bool operator ==(const VRleImpl &) const;
+    void intersected(const VRect &r, VRleImpl &result);
+    void intersected(const VRleImpl &clip, VRleImpl &result);
+    friend VDebug& operator<<(VDebug& os, const VRleImpl& object);
+    void invert();
+    void alphaMul(int alpha);
+    void translate(const VPoint &pt);
+    void opAdd(const VRleImpl &other, VRleImpl &res);
+    VRect bbox();
+public:
+    VRect                   m_bbox;
+    std::vector<VRle::Span> m_spans;// array of Spanlines.
+    VPoint                  mOffset;
+    bool                     mBboxDirty;
+};
+
+inline static void
+copyArrayToVector(const VRle::Span *span, int count, std::vector<VRle::Span> &v)
+{
+    // make sure enough memory available
+    v.reserve(v.size() + count);
+    std::copy(span, span + count, back_inserter(v));
+}
+
+VDebug& operator<<(VDebug& os, const VRleImpl& o)
+{
+    os<<"[bbox="<< o.m_bbox<<"]"<<"[offset="<<o.mOffset<<"]"<<
+        "[span count ="<<o.m_spans.size()<<"]\n";
+    os<<"[rle spans = {x y len coverage}";
+    for(auto sp : o.m_spans)
+        os<<"{"<<sp.x<<" "<<sp.y<<" "<<sp.len<<" "<<sp.coverage<<"}";
+    os<<"]";
+    return os;
+}
+
+VRect VRleImpl::bbox()
+{
+    updateBbox();
+    return m_bbox;
+}
+
+void VRleImpl::translate(const VPoint &pt)
+{
+    //take care of last offset if applied
+    mOffset = pt - mOffset;
+    int x = mOffset.x();
+    int y = mOffset.y();
+    for (auto &i : m_spans) {
+        i.x = i.x + x;
+        i.y = i.y + y;
+    }
+    updateBbox();
+    m_bbox.translate(mOffset.x(), mOffset.y());
+}
+
+void VRleImpl::invert()
+{
+    for (auto &i : m_spans) {
+        i.coverage = 255 - i.coverage;
+    }
+}
+
+void VRleImpl::alphaMul(int alpha)
+{
+    alpha &= 0xff;
+
+    for (auto &i : m_spans) {
+        i.coverage = divBy255(i.coverage * alpha);
+    }
+}
+
+void VRleImpl::intersected(const VRect &r, VRleImpl &result)
+{
+    VRect clip = r;
+
+    VRleHelper tresult, tmp_obj;
+    std::array<VRle::Span,256> array;
+
+    //setup the tresult object
+    tresult.size = array.size();
+    tresult.alloc = array.size();
+    tresult.spans = array.data();
+
+    // setup tmp object
+    tmp_obj.size = m_spans.size();
+    tmp_obj.spans = m_spans.data();
+
+    // run till all the spans are processed
+    while (tmp_obj.size)
+      {
+         rleIntersectWithRect(clip, &tmp_obj, &tresult);
+         if (tresult.size) {
+             copyArrayToVector(tresult.spans, tresult.size, result.m_spans);
+         }
+         tresult.size = 0;
+      }
+    result.updateBbox();
+}
+
+void VRleImpl::intersected(const VRleImpl &clip, VRleImpl &result)
+{
+    VRleHelper tresult, tmp_obj, tmp_clip;
+    std::array<VRle::Span,256> array;
+
+    //setup the tresult object
+    tresult.size = array.size();
+    tresult.alloc = array.size();
+    tresult.spans = array.data();
+
+    // setup tmp object
+    tmp_obj.size = m_spans.size();
+    tmp_obj.spans = m_spans.data();
+
+    //setup tmp clip object
+    tmp_clip.size = clip.m_spans.size();
+    tmp_clip.spans = const_cast<VRle::Span *>(clip.m_spans.data());
+
+    // run till all the spans are processed
+    while (tmp_obj.size)
+      {
+         rleIntersectWithRle(&tmp_clip, 0, 0, &tmp_obj, &tresult);
+         if (tresult.size) {
+             copyArrayToVector(tresult.spans, tresult.size, result.m_spans);
+         }
+         tresult.size = 0;
+      }
+    result.updateBbox();
+}
+
+void VRleImpl::opAdd(const VRleImpl &other, VRleImpl &result)
+{
+    // reserve some space for the result vector.
+    result.m_spans.reserve(m_spans.size() + other.m_spans.size());
+    // if two rle are disjoint
+    if (!m_bbox.intersects(other.m_bbox)) {
+        result.m_spans = m_spans;
+        copyArrayToVector(other.m_spans.data(), other.m_spans.size(), result.m_spans);
+    } else {
+        VRle::Span *ptr = m_spans.data();
+        int otherY = other.m_bbox.top();
+        // 1. forward till both y intersect
+        while (ptr->y < otherY)  ptr++;
+        int spanToCopy = ptr - m_spans.data();
+        copyArrayToVector(m_spans.data(), spanToCopy, result.m_spans);
+
+        // 2. calculate the intersect region
+        VRleHelper tresult, tmp_obj, tmp_other;
+        std::array<VRle::Span,256> array;
+
+        //setup the tresult object
+        tresult.size = array.size();
+        tresult.alloc = array.size();
+        tresult.spans = array.data();
+
+        // setup tmp object
+        tmp_obj.size = m_spans.size() - spanToCopy;
+        tmp_obj.spans = ptr;
+
+        //setup tmp clip object
+        tmp_other.size = other.m_spans.size();
+        tmp_other.spans = const_cast<VRle::Span *>(other.m_spans.data());
+
+        // run till all the spans are processed
+        while (tmp_obj.size && tmp_other.size)
+          {
+             rleAddWithRle1(&tmp_other, &tmp_obj, &tresult);
+             if (tresult.size) {
+                 copyArrayToVector(tresult.spans, tresult.size, result.m_spans);
+             }
+             tresult.size = 0;
+          }
+        //3. copy the rest
+        if (tmp_other.size) {
+            copyArrayToVector(tmp_other.spans, tmp_other.size, result.m_spans);
+        }
+        if (tmp_obj.size) {
+            copyArrayToVector(tmp_obj.spans, tmp_obj.size, result.m_spans);
+        }
+    }
+
+    // update result bounding box
+    VRegion reg(m_bbox);
+    reg += other.m_bbox;
+    result.m_bbox = reg.boundingRect();
+    result.mBboxDirty = false;
+}
+
+
+VRleImpl &VRleImpl::operator=(const VRleImpl &other)
+{
+    m_spans = other.m_spans;
+    m_bbox = other.m_bbox;
+    mOffset = other.mOffset;
+    return *this;
+}
+
+bool VRleImpl::operator ==(const VRleImpl &other) const
+{
+    if (m_spans.size() != other.m_spans.size())
+        return false;
+    const VRle::Span *spans = m_spans.data();
+    const VRle::Span *o_spans = other.m_spans.data();
+    int sz = m_spans.size();
+
+    for (int i = 0; i < sz; i++) {
+        if (spans[i].x != o_spans[i].x ||
+            spans[i].y != o_spans[i].y ||
+            spans[i].len != o_spans[i].len ||
+            spans[i].coverage != o_spans[i].coverage)
+            return false;
+    }
+    return true;
+}
+
+
+void VRleImpl::updateBbox()
+{
+    if (!mBboxDirty) return;
+
+    mBboxDirty = false;
+
+    int i, l = 0, t = 0, r = 0, b = 0, sz;
+    l = std::numeric_limits<int>::max();
+    const VRle::Span *span = m_spans.data();
+
+    m_bbox = VRect();
+    sz = m_spans.size();
+    if (sz)
+      {
+         t = span[0].y;
+         b = span[sz-1].y;
+         for (i = 0; i < sz; i++)
+           {
+              if (span[i].x < l) l = span[i].x;
+              if (span[i].x + span[i].len > r) r = span[i].x + span[i].len;
+           }
+         m_bbox = VRect(l, t, r - l, b - t + 1);
+      }
+}
+
+void VRleImpl::addSpan(const VRle::Span *span, int count)
+{
+    copyArrayToVector(span, count, m_spans);
+    mBboxDirty = true;
+}
+
+struct VRleData
+{
+    RefCount    ref;
+    VRleImpl   impl;
+};
+
+static const struct VRleData shared_empty = {RefCount(-1),
+                                              VRleImpl()};
+
+inline void VRle::cleanUp(VRleData *d)
+{
+    delete d;
+}
+
+void VRle::detach()
+{
+    if (d->ref.isShared())
+        *this = copy();
+}
+
+VRle VRle::copy() const
+{
+    VRle other;
+
+    other.d = new VRleData;
+    other.d->impl = d->impl;
+    other.d->ref.setOwned();
+    return other;
+}
+
+VRle::~VRle()
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+}
+
+VRle::VRle()
+    : d(const_cast<VRleData*>(&shared_empty))
+{
+}
+
+VRle::VRle(const VRle &other)
+{
+    d = other.d;
+    d->ref.ref();
+}
+
+VRle::VRle(VRle &&other): d(other.d)
+{
+    other.d = const_cast<VRleData*>(&shared_empty);
+}
+
+VRle &VRle::operator=(const VRle &other)
+{
+    other.d->ref.ref();
+    if (!d->ref.deref())
+        cleanUp(d);
+
+    d = other.d;
+    return *this;
+}
+
+inline VRle &VRle::operator=(VRle &&other)
+{
+    if (!d->ref.deref())
+        cleanUp(d);
+    d = other.d;
+    other.d = const_cast<VRleData*>(&shared_empty);
+    return *this;
+}
+
+bool VRle::isEmpty()const
+{
+    return (d == &shared_empty || d->impl.m_spans.empty());
+}
+
+void VRle::addSpan(const VRle::Span *span, int count)
+{
+    detach();
+    d->impl.addSpan(span, count);
+}
+
+VRect VRle::boundingRect() const
+{
+    if(isEmpty())
+        return VRect();
+    return d->impl.bbox();
+}
+
+bool VRle::operator ==(const VRle &other) const
+{
+    if (isEmpty())
+        return other.isEmpty();
+    if (other.isEmpty())
+        return isEmpty();
+
+    if (d == other.d)
+        return true;
+    else
+        return d->impl == other.d->impl;
+}
+
+void VRle::translate(const VPoint &p)
+{
+    if (isEmpty()) return;
+
+    if (d->impl.mOffset == p) return;
+
+    detach();
+    d->impl.translate(p);
+}
+
+VRle VRle::intersected(const VRect &r) const
+{
+    if (isEmpty() || r.isEmpty())
+        return VRle();
+
+    // check if the bounding rect is contain inside r
+    if (r.contains(boundingRect(), true))
+        return *this;
+
+    VRle result;
+    result.detach();
+    d->impl.intersected(r, result.d->impl);
+    return result;
+}
+
+VRle VRle::intersected(const VRle &other) const
+{
+    if (isEmpty() || other.isEmpty())
+        return VRle();
+    // check if the bounding rect are not intersecting
+    VRle result;
+    result.detach();
+    d->impl.intersected(other.d->impl, result.d->impl);
+    return result;
+}
+
+VRle VRle::operator~() const
+{
+    if (isEmpty()) return VRle();
+
+    VRle result = *this;
+    result.detach();
+    result.d->impl.invert();
+    return result;
+}
+
+VRle VRle::operator+(const VRle &other) const
+{
+    if (isEmpty()) return other;
+
+    if (other.isEmpty()) return *this;
+
+    VRle result;
+    result.detach();
+    if (boundingRect().top() < other.boundingRect().top())
+       d->impl.opAdd(other.d->impl, result.d->impl);
+    else
+        other.d->impl.opAdd(d->impl, result.d->impl);
+    return result;
+}
+
+VRle VRle::operator-(const VRle &other) const
+{
+    if (isEmpty()) return ~other;
+
+    if (other.isEmpty()) return *this;
+
+    VRle temp = ~other;
+    return *this + temp;
+}
+
+VRle VRle::operator&(const VRle &o) const
+{
+    if (isEmpty() || o.isEmpty()) return VRle();
+
+    if (!boundingRect().intersects(o.boundingRect())) return VRle();
+
+    VRle result;
+    result.detach();
+    d->impl.intersected(o.d->impl, result.d->impl);
+    return result;
+}
+
+
+
+void VRle::intersected(const VRect &r, VRleSpanCb cb, void *userData)
+{
+    //TODO Implement
+}
+
+
+VRle  &VRle::intersect(const VRect &r)
+{
+    if (isEmpty() || r.isEmpty())
+        return *this = VRle();
+
+    VRle result;
+    result.detach();
+    d->impl.intersected(r, result.d->impl);
+    return *this = result;
+}
+
+VRle operator*(const VRle &obj, int alpha)
+{
+    if (obj.isEmpty()) return obj;
+
+    VRle result = obj;
+    result.detach();
+    result.d->impl.alphaMul(alpha);
+    return result;
+}
+
+
+int VRle::size() const
+{
+    if (isEmpty()) return 0;
+    return d->impl.m_spans.size();
+}
+
+const VRle::Span* VRle::data() const
+{
+    if (isEmpty()) return nullptr;
+    return d->impl.m_spans.data();
+}
+
+VRle VRle::toRle(const VRectF &rect)
+{
+    VRle result;
+    result.detach();
+    int x = rect.left();
+    int y = rect.top();
+    int width = rect.width();
+    int height = rect.height();
+    result.d->impl.m_spans.reserve(height);
+    VRle::Span span;
+    for(int i=0; i < height ; i++) {
+        span.x = x;
+        span.y = y + i;
+        span.len = width;
+        span.coverage = 255;
+       result.d->impl.m_spans.push_back(span);
+    }
+    return result;
+}
+
+VDebug& operator<<(VDebug& os, const VRle& o)
+{
+    os<<"[RLE: [dptr = "<<"o.d"<<"]"<<"[ref = "<<o.d->ref.count()<<"]"<<o.d->impl<<"]";
+    return os;
+}
+
+
+
+
+
diff --git a/src/vector/vrle.h b/src/vector/vrle.h
new file mode 100644 (file)
index 0000000..4697b1f
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef VRLE_H
+#define VRLE_H
+#include<vglobal.h>
+#include<vrect.h>
+#include<vpoint.h>
+
+struct VRleData;
+class VRle
+{
+public:
+    struct Span
+    {
+      short  x;
+      short  y;
+      ushort len;
+      uchar  coverage;
+    };
+    typedef void (*VRleSpanCb)(int count, const VRle::Span *spans, void *userData);
+    ~VRle();
+    VRle();
+    VRle(const VRle &other);
+    VRle(VRle &&other);
+    VRle &operator=(const VRle &);
+    VRle &operator=(VRle &&other);
+    bool isEmpty()const;
+    VRect boundingRect() const;
+    void addSpan(const VRle::Span *span, int count);
+    bool operator ==(const VRle &other) const;
+    void translate(const VPoint &p);
+    void translate(int x, int y);
+    VRle intersected(const VRect &r) const;
+    VRle intersected(const VRle &other) const;
+    void intersected(const VRect &r, VRleSpanCb cb, void *userData);
+    VRle &intersect(const VRect &r);
+    int size() const;
+    const VRle::Span* data() const;
+    VRle operator~() const;
+    VRle operator+(const VRle &o) const;
+    VRle operator-(const VRle &o) const;
+    VRle operator&(const VRle &o) const;
+    static VRle toRle(const VRectF &rect);
+    friend VRle operator*(const VRle &, int alpha);
+    inline friend VRle operator*(int alpha, const VRle &);
+    friend VDebug& operator<<(VDebug& os, const VRle& object);
+private:
+    VRle copy() const;
+    void detach();
+    void cleanUp(VRleData *x);
+    VRleData *d;
+};
+
+inline void VRle::translate(int x, int y)
+{
+    translate(VPoint(x,y));
+}
+
+inline VRle operator*(int alpha, const VRle &rle)
+{
+    return (rle * alpha);
+}
+
+#endif // VRLE_H
diff --git a/test/meson.build b/test/meson.build
new file mode 100644 (file)
index 0000000..7388740
--- /dev/null
@@ -0,0 +1,20 @@
+
+test_sources_util = ['ssgtestsuite.cpp',
+                     'testsgregion.cpp'
+                    ]
+test_sources_ftraster = ['testftraster.cpp'
+                        ]
+
+test_sources =[]
+test_sources += test_sources_util
+test_sources += test_sources_ftraster
+
+test_dep = dependency('gtest')
+
+ssg_utest = executable('testexe',
+                       test_sources,
+                       include_directories : inc,
+                       link_with : lottie_player_lib_version,
+                       dependencies : test_dep)
+
+test('ssgsuite test', ssg_utest)
diff --git a/test/ssgtestsuite.cpp b/test/ssgtestsuite.cpp
new file mode 100644 (file)
index 0000000..5fa62d3
--- /dev/null
@@ -0,0 +1,8 @@
+#include <gtest/gtest.h>
+#include"vdebug.h"
+
+int main(int argc, char **argv) {
+    initialize(GuaranteedLogger(), "/tmp/", "ssglog", 1);
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/test/testftraster.cpp b/test/testftraster.cpp
new file mode 100644 (file)
index 0000000..4743b15
--- /dev/null
@@ -0,0 +1,30 @@
+#include <gtest/gtest.h>
+#include "vpath.h"
+#include "vraster.h"
+
+class VRasterTest : public ::testing::Test {
+public:
+    void SetUp()
+    {
+        path.moveTo(VPointF(0,0));
+        path.lineTo(VPointF(10,0));
+        path.lineTo(VPointF(10,10));
+        path.lineTo(VPointF(0,10));
+        path.close();
+        pathRect = VRect(0,0,10,10);
+    }
+    void TearDown()
+    {
+
+    }
+public:
+  VPath path;
+  VRect pathRect;
+};
+
+
+TEST_F(VRasterTest, constructor) {
+    FTOutline *outline = VRaster::toFTOutline(path);
+    ASSERT_TRUE(outline != nullptr);
+    VRaster::deleteFTOutline(outline);
+}
diff --git a/test/testsgregion.cpp b/test/testsgregion.cpp
new file mode 100644 (file)
index 0000000..ecaae0f
--- /dev/null
@@ -0,0 +1,120 @@
+#include <gtest/gtest.h>
+#include"vregion.h"
+#include"vdebug.h"
+#include"vpoint.h"
+class VRegionTest : public ::testing::Test {
+public:
+    VRegionTest():rgn1(-10, -10, 20, 20)
+    {
+    }
+    void SetUp()
+    {
+        rect1 = VRect(-10, -10, 20, 20);
+        rect2 = VRect(-15, 5, 10, 10);
+        rgn2 += rect2;
+        rgn3 = rgn1;
+    }
+    void TearDown()
+    {
+
+    }
+public:
+    VRegion emptyRgn;
+    VRegion rgn1;
+    VRegion rgn2;
+    VRegion rgn3;
+    VRect rect1;
+    VRect rect2;
+    VRect rect3;
+
+};
+
+TEST_F(VRegionTest, constructor) {
+    ASSERT_EQ(rgn1.rectCount() , 1);
+    ASSERT_TRUE(rgn1.rectAt(0) == rect1);
+    ASSERT_TRUE(rgn1==rgn3);
+    ASSERT_TRUE(rgn1!=rgn2);
+}
+
+TEST_F(VRegionTest, moveSemantics) {
+    // move assignment
+
+    rgn1 = rect1;
+    VRegion tmp;
+    tmp = std::move(rgn1);
+    ASSERT_TRUE(rgn1.isEmpty());
+
+    // move construction
+    rgn1 = rect1;
+    VRegion mvrgn = std::move(rgn1);
+    ASSERT_TRUE(rgn1.isEmpty());
+    ASSERT_TRUE(mvrgn == rect1);
+}
+TEST_F(VRegionTest, isEmpty) {
+    ASSERT_TRUE(emptyRgn.isEmpty());
+    ASSERT_TRUE(emptyRgn == VRegion());
+    ASSERT_TRUE(emptyRgn.rectCount() == 0);
+    ASSERT_TRUE(emptyRgn.boundingRect() == VRect());
+}
+
+TEST_F(VRegionTest, boundingRect) {
+    {
+        VRect rect;
+        VRegion region(rect);
+        ASSERT_TRUE(region.boundingRect() == rect);
+    }
+    {
+        VRect rect(10, -20, 30, 40);
+        VRegion region(rect);
+        ASSERT_TRUE(region.boundingRect() == rect);
+    }
+    {
+        VRect rect(15,25,10,10);
+        VRegion region(rect);
+        ASSERT_TRUE(region.boundingRect() == rect);
+    }
+}
+
+TEST_F(VRegionTest, swap) {
+    VRegion r1(VRect(0, 0,10,10));
+    VRegion r2(VRect(10,10,10,10));
+    std::swap(r1 ,r2);
+    ASSERT_TRUE(r1.rectAt(0) == VRect(10,10,10,10));
+    ASSERT_TRUE(r2.rectAt(0) == VRect(0, 0,10,10));
+}
+
+TEST_F(VRegionTest, substracted) {
+    VRegion r1(VRect(0, 0,20,20));
+    VRegion r2 = r1.subtracted(VRect(5,5,5,5));
+    VRegion expected;
+    expected += VRect(0,0,20,5);
+    expected += VRect(0,5,5,5);
+    expected += VRect(10,5,10,5);
+    expected += VRect(0,10,20,10);
+    ASSERT_TRUE(r2.rectCount() == expected.rectCount());
+    ASSERT_TRUE(r2 == expected);
+    r2 += VRect(5,5,5,5);
+    ASSERT_TRUE(r2 == r1);
+}
+
+TEST_F(VRegionTest, translate) {
+    VRegion r1(VRect(0, 0,20,20));
+    VPoint offset(10,10);
+    VRegion r2 =  r1.translated(offset);
+    r1.translate(offset);
+    ASSERT_TRUE(r2 == r2);
+}
+
+TEST_F(VRegionTest, intersects) {
+    VRegion r1(VRect(0, 0,20,20));
+    VRegion r2(VRect(20, 20,10,10));
+    ASSERT_FALSE(r1.intersects(r2));
+    r2 += VRect(5, 0,20,20);
+    ASSERT_TRUE(r1.intersects(r2));
+}
+
+TEST_F(VRegionTest, contains) {
+    VRegion r1(VRect(0, 0,20,20));
+    ASSERT_TRUE(r1.contains(VRect(5,5,10,10)));
+    ASSERT_FALSE(r1.contains(VRect(11,5,10,10)));
+}